用VSCode开发一个asp.net core2.0+angular5项目(5): Angular5+asp.net core 2.0 web api文件上传...
第一部分: http://www.cnblogs.com/cgzl/p/8478993.html
第二部分: http://www.cnblogs.com/cgzl/p/8481825.html
第三部分: https://www.cnblogs.com/cgzl/p/8525541.html
第四部分: https://www.cnblogs.com/cgzl/p/8536350.html
这部分就讲从angular5的客户端上传图片到asp.net core 2.0的 web api.
这是需要的源码: https://pan.baidu.com/s/1Eqc4MRiQDwOHmu0OHyttqA
当前的效果如下:
点击这个超链接后:
好的, 下面开始编写上传相关的代码.
Asp.net core 2.0 文件上传
按照顺序, 先建立Photo的domain model:
建立Models/Photo.cs:
using System.ComponentModel.DataAnnotations;namespace Tv.Models {public class Photo{public int Id { get; set; }[Required][StringLength(255)]public string FileName { get; set; }} }
然后编辑TvShow.cs:
using System.Collections.Generic; using System.ComponentModel.DataAnnotations;namespace Tv.Models {public class TvShow{public TvShow(){Photoes = new List<Photo>();}public int Id { get; set; }[Required][StringLength(50)]public string Name { get; set; }public int TvNetworkId { get; set; }public TvNetwork TvNetwork { get; set; }public ICollection<Photo> Photoes { get; set; }} }
TvContext.cs:
using Microsoft.EntityFrameworkCore; using Tv.Models;namespace Tv.Database {public class TvContext : DbContext{public TvContext(DbContextOptions<TvContext> options): base(options){}public DbSet<TvNetwork> TvNetworks { get; set; }public DbSet<TvShow> TvShows { get; set; }public DbSet<Photo> Photoes { get; set; }} }
然后添加迁移和更新数据库, 您应该知道怎么做了, 这部分就略了.
添加PhotoViewModel.cs:
namespace Tv.ViewModels {public class PhotoViewModel{public int Id { get; set; }public string FileName { get; set; }} }
不要忘了做一下Mapping映射, 这里我就不写了.
然后建立PhotoesController.cs:
using System; using System.IO; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Tv.Database; using Tv.Models; using Tv.ViewModels;namespace Tv.Controllers {[Route("api/tvshows/{tvShowId}/photoes")]public class PhotoesController : Controller{private readonly IHostingEnvironment host;private readonly ITvRepository tvRepository;private readonly IUnitOfWork unitOfWork;private readonly IMapper mapper;public PhotoesController(IHostingEnvironment host, ITvRepository tvRepository, IUnitOfWork unitOfWork, IMapper mapper){this.host = host;this.tvRepository = tvRepository;this.unitOfWork = unitOfWork;this.mapper = mapper;}[HttpPost]public async Task<IActionResult> Upload(int tvShowId, IFormFile file){var tvShow = await tvRepository.GetTvShowByIdAsync(tvShowId, includeRelated: false);if (tvShow == null){return NotFound();}var uploadsFolderPath = Path.Combine(host.WebRootPath, "Uploads");if (!Directory.Exists(uploadsFolderPath)){Directory.CreateDirectory(uploadsFolderPath);}var fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);var filePath = Path.Combine(uploadsFolderPath, fileName);using (var stream = new FileStream(filePath, FileMode.Create)){await file.CopyToAsync(stream);}var photo = new Photo{FileName = fileName};tvShow.Photoes.Add(photo);await unitOfWork.SaveAsync();var result = mapper.Map<Photo, PhotoViewModel>(photo);return Ok(result);}} }
这里要简单讲一下. asp.net core 上传文件的文档在这: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads
首先该controller的路由应该遵循web api的规范, 注意controller 的路由地址.
上传单个文件需要使用IFormFile作为Action的参数. 如果上传的是多个文件, 那么应该使用IFormCollection.
这里我做的是单文件上传, 所以使用IFormFile.
随后使用注入的IHostingEnvironment获得wwwroot目录, 我想要把文件上传到wwwroot/uploads下, 判断该目录是否存在, 如果不存在则创建该目录.
为了防黑, 把文件名改成Guid, 后缀名不变.
然后使用FileStream创建该文件.
后边的内容就是把文件名保存到数据库了.
接下来, 使用Postman来测试这个api.
打开postman, 按照图示输入:
注意这里的参数的key为file, 这个名字要与action的参数名一致:
send:
很好, 测试通过.
下面为Action添加一些验证:
这就是一些常规的验证, 没有什么特别的, 就不累述了.
针对这些东西, 您可以使用配置类, 并把相关的值放在appSettings.json里面. 这部分您自己学一下吧 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?tabs=basicconfiguration.
下面是客户端
Angular 5 文件上传
先做ui, tv-show-detail.component.html:
<form><h2>基本信息</h2><div class="form-group row"><label for="name" class="col-sm-2 col-form-label">名称</label><div class="col-sm-10"><input type="text" readonly class="form-control-plaintext" id="name" value="{{model.name}}"></div></div><h2>电视剧照片</h2><div class="form-group row"><label for="file" class="col-sm-2 col-form-label">照片</label><input type="file" name="file" id="file" class="form-control" #fileInput (change)="upload()"></div> </form>
注意这里使用了template reference.
然后创建一个photo.service:
import { Injectable } from '@angular/core'; import { HttpHeaders, HttpClient } from '@angular/common/http';@Injectable() export class PhotoService {constructor(private http: HttpClient) { }upload(tvShowId: number, photo) {const formData = new FormData();formData.append('file', photo);return this.http.post(`/api/tvshows/${tvShowId}/photoes`, formData);} }
其中post的参数类型是FormData, 它是js原生对象. formData里面文件的key要和后台Action方法的参数名一样.
最后改一下tv-show-detail.component.ts:
import { Component, OnInit, ElementRef, ViewChild } from '@angular/core'; import { TvShowService } from '../../services/tv-show.service'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { TvShow } from '../../models/tv-show'; import { Subscription } from 'rxjs/Subscription'; import { ToastrService } from 'ngx-toastr'; import { PhotoService } from '../../services/photo.service';@Component({selector: 'app-tv-show-detail',templateUrl: './tv-show-detail.component.html',styleUrls: ['./tv-show-detail.component.css'] }) export class TvShowDetailComponent implements OnInit {tvShowId: number;@ViewChild('fileInput') fileInput: ElementRef;model: TvShow = new TvShow();busy: Subscription;constructor(private tvShowService: TvShowService,private router: Router,private route: ActivatedRoute,private toastr: ToastrService,private photoService: PhotoService) { }ngOnInit() {this.route.paramMap.switchMap((params: ParamMap) => {this.tvShowId = +params.get('id');return this.tvShowService.getById(this.tvShowId);}).subscribe(item => {this.model = item;});}upload() {const ele = this.fileInput.nativeElement;this.photoService.upload(this.tvShowId, ele.files[0]).subscribe(x => {console.log(x);});} }
如果上传成功, 那么回来先只做打印到log. 试一下:
上传成功. 文件即出现在wwwroot下, 文件名也保存到了数据库.
回显照片:
首先修改Photo.cs:
using System.ComponentModel.DataAnnotations;namespace Tv.Models {public class Photo{public int Id { get; set; }[Required][StringLength(255)]public string FileName { get; set; }public int TvShowId { get; set; }public TvShow TvShow { get; set; }} }
不要忘记迁移数据库.
然后创建Repository, 并注册:
using System.Collections.Generic; using System.Threading.Tasks; using Tv.Models;namespace Tv.Database {public interface IPhotoRepository{Task<List<Photo>> GetPhotoesByTvShowIdAsync(int tvShowId);} }
using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Tv.Models;namespace Tv.Database {public class PhotoRepository : IPhotoRepository{private readonly TvContext context;public PhotoRepository(TvContext context){this.context = context;}public async Task<List<Photo>> GetPhotoesByTvShowIdAsync(int tvShowId){var photoes = await context.Photoes.Where(x => x.TvShowId == tvShowId).ToListAsync();return photoes;}} }
最后修改PhotoesController:
using System; using System.IO; using System.Linq; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Tv.Database; using Tv.Models; using Tv.ViewModels;namespace Tv.Controllers {[Route("api/tvshows/{tvShowId}/photoes")]public class PhotoesController : Controller{private readonly IHostingEnvironment host;private readonly ITvRepository tvRepository;private readonly IUnitOfWork unitOfWork;private readonly IMapper mapper;private readonly IPhotoRepository photoRepository;public PhotoesController(IHostingEnvironment host, ITvRepository tvRepository, IUnitOfWork unitOfWork, IMapper mapper, IPhotoRepository photoRepository){this.host = host;this.tvRepository = tvRepository;this.unitOfWork = unitOfWork;this.mapper = mapper;this.photoRepository = photoRepository;}[HttpPost]public async Task<IActionResult> Upload(int tvShowId, IFormFile file){var tvShow = await tvRepository.GetTvShowByIdAsync(tvShowId, includeRelated: false);if (tvShow == null){return NotFound();}if (file == null){return BadRequest("File is null");}if (file.Length == 0){return BadRequest("File is Empty");}if (file.Length > 10 * 1024 * 1024){return BadRequest("文件大小不能超过10M");}var acceptedTypes = new[] { ".jpg", ".png", ".jpeg" };if (acceptedTypes.All(t => t != Path.GetExtension(file.FileName).ToLower())){return BadRequest("文件类型不对");}var uploadsFolderPath = Path.Combine(host.WebRootPath, "Uploads");if (!Directory.Exists(uploadsFolderPath)){Directory.CreateDirectory(uploadsFolderPath);}var fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);var filePath = Path.Combine(uploadsFolderPath, fileName);using (var stream = new FileStream(filePath, FileMode.Create)){await file.CopyToAsync(stream);}var photo = new Photo{FileName = fileName};tvShow.Photoes.Add(photo);await unitOfWork.SaveAsync();var result = mapper.Map<Photo, PhotoViewModel>(photo);return Ok(result);}[HttpGet]public async Task<IActionResult> GetPhotoesByTvShowId(int tvShowId){var photoes = await photoRepository.GetPhotoesByTvShowIdAsync(tvShowId);return Ok(photoes);}} }
然后修改angular部分:
添加Photo到model:
export class Photo {id: number;tvShowId: number;fileName: string; }
修改photo service:
import { Injectable } from '@angular/core'; import { HttpHeaders, HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { Photo } from '../models/photo';@Injectable() export class PhotoService {constructor(private http: HttpClient) { }upload(tvShowId: number, photo): Observable<Photo> {const formData = new FormData();formData.append('file', photo);return this.http.post<Photo>(`/api/tvshows/${tvShowId}/photoes`, formData);}getPhotoes(tvShowId: number): Observable<Photo[]> {return this.http.get<Photo[]>(`/api/tvshows/${tvShowId}/photoes`);} }
tv-show-detail.component.html:
<form><h2>基本信息</h2><div class="form-group row"><label for="name" class="col-sm-2 col-form-label">名称</label><div class="col-sm-10"><input type="text" readonly class="form-control-plaintext" id="name" value="{{model.name}}"></div></div><h2>电视剧照片</h2><div class="form-group row"><label for="file" class="col-sm-2 col-form-label">照片</label><input type="file" name="file" id="file" class="form-control" #fileInput (change)="upload()"></div><div><img [src]="'http://localhost:5000/Uploads/' + p.fileName" [alt]="p.fileName" *ngFor="let p of photoes" class="m-1" width="200"height="200" /></div> </form>
tv-show-detail.component.ts:
import { Component, OnInit, ElementRef, ViewChild } from '@angular/core'; import { TvShowService } from '../../services/tv-show.service'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { TvShow } from '../../models/tv-show'; import { Subscription } from 'rxjs/Subscription'; import { ToastrService } from 'ngx-toastr'; import { PhotoService } from '../../services/photo.service'; import { Photo } from '../../models/photo'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/forkJoin';@Component({selector: 'app-tv-show-detail',templateUrl: './tv-show-detail.component.html',styleUrls: ['./tv-show-detail.component.css'] }) export class TvShowDetailComponent implements OnInit {tvShowId: number;@ViewChild('fileInput') fileInput: ElementRef;model: TvShow = new TvShow();busy: Subscription;photoes: Photo[] = [];constructor(private tvShowService: TvShowService,private router: Router,private route: ActivatedRoute,private toastr: ToastrService,private photoService: PhotoService) { }ngOnInit() {this.route.paramMap.switchMap((params: ParamMap) => {this.tvShowId = +params.get('id');return Observable.forkJoin<TvShow, Photo[]>(this.tvShowService.getById(this.tvShowId),this.photoService.getPhotoes(this.tvShowId));}).subscribe(([tvShow, photoes]) => {this.model = tvShow;this.photoes = photoes;});}upload() {const ele = this.fileInput.nativeElement;this.photoService.upload(this.tvShowId, ele.files[0]).subscribe(photo => {this.photoes.push(photo);});} }
这部分比较简单, 注意同时发送多个请求可以使用forkJoin.
看看效果:
如果照片没有显示出来, 可能是asp.net core没有启用静态文件到支持, 在Startup.cs添加这句话即可:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Tv.Database;namespace Tv {public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){services.AddAutoMapper();// services.AddDbContext<TvContext>(opt => opt.UseSqlServer(Configuration["ConnectionStrings:Default"]));services.AddDbContext<TvContext>(opt => opt.UseSqlServer(Configuration.GetConnectionString("Default")));services.AddScoped<ITvRepository, TvRepository>();services.AddScoped<IPhotoRepository, PhotoRepository>();services.AddScoped<IUnitOfWork, UnitOfWork>();services.AddMvc();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseStaticFiles();app.UseMvc();}} }
很好. 即使是刚添加完到照片也会即时显示出来.
上传进度显示.
首先创建一个修改photo service:
根据官方文档, 如果想要上传文件时显示进度, 那么应该使用HttpRequest, 并设置属性reportProgress为true:
import { Injectable } from '@angular/core'; import { HttpHeaders, HttpClient, HttpRequest, HttpEvent, HttpEventType, HttpErrorResponse } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { Photo } from '../models/photo';@Injectable() export class PhotoService {constructor(private http: HttpClient) { }upload(tvShowId: number, photo: File) {const formData = new FormData();formData.append('file', photo);// return this.http.post<Photo>(`/api/tvshows/${tvShowId}/photoes`, formData);const req = new HttpRequest('POST', `/api/tvshows/${tvShowId}/photoes`, formData, {reportProgress: true});return this.http.request<Photo>(req);}getPhotoes(tvShowId: number): Observable<Photo[]> {return this.http.get<Photo[]>(`/api/tvshows/${tvShowId}/photoes`);} }
回到 tv-show-detail.component.ts:
import { Component, OnInit, ElementRef, ViewChild } from '@angular/core'; import { TvShowService } from '../../services/tv-show.service'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { TvShow } from '../../models/tv-show'; import { Subscription } from 'rxjs/Subscription'; import { ToastrService } from 'ngx-toastr'; import { PhotoService } from '../../services/photo.service'; import { Photo } from '../../models/photo'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/forkJoin'; import { HttpEvent, HttpEventType } from '@angular/common/http'; import { HttpResponse } from 'selenium-webdriver/http';@Component({selector: 'app-tv-show-detail',templateUrl: './tv-show-detail.component.html',styleUrls: ['./tv-show-detail.component.css'] }) export class TvShowDetailComponent implements OnInit {tvShowId: number;@ViewChild('fileInput') fileInput: ElementRef;model: TvShow = new TvShow();busy: Subscription;photoes: Photo[] = [];constructor(private tvShowService: TvShowService,private router: Router,private route: ActivatedRoute,private toastr: ToastrService,private photoService: PhotoService) { }ngOnInit() {this.route.paramMap.switchMap((params: ParamMap) => {this.tvShowId = +params.get('id');return Observable.forkJoin<TvShow, Photo[]>(this.tvShowService.getById(this.tvShowId),this.photoService.getPhotoes(this.tvShowId));}).subscribe(([tvShow, photoes]) => {this.model = tvShow;this.photoes = photoes;});}upload() {const ele = this.fileInput.nativeElement;const file = ele.files[0];this.photoService.upload(this.tvShowId, file).subscribe((event: HttpEvent<any>) => {switch (event.type) {case HttpEventType.Sent:console.log(`开始上传 "${file.name}", 大小是: ${file.size}.`);break;case HttpEventType.UploadProgress:const percentDone = Math.round(100 * event.loaded / event.total);console.log(`文件 "${file.name}" 的上传进度是 ${percentDone}%.`);break;case HttpEventType.Response:console.log(`文件 "${file.name}" 上传成功!`);this.toastr.success(`文件 "${file.name}" 上传成功!`);this.photoes.push(<Photo>(event.body));break;default:console.log(`文件 "${file.name}" 的事件类型: ${event.type}.`);break;}});} }
这样, 上传文件时, 每个进度都会返回一个event, 我暂时就先把它打印到控制台.
看一下效果:
好的, 文件太小, 本地到速度又太快, 进度直接100%了.
那我改一下Chrome的设置, 打开Developer Tools的Network 选项, 然后点击这里:
然后添加:
添加一个非常慢的网速限制:
最后选取这个限制:
实际上, 选择Slow 3G就很慢了.
这时, 再上传一次试试效果:
很好, 没问题.
接下来就是UI显示进度条的问题了, 很简单:
打开html:
<form><h2>基本信息</h2><div class="form-group row"><label for="name" class="col-sm-2 col-form-label">名称</label><div class="col-sm-10"><input type="text" readonly class="form-control-plaintext" id="name" value="{{model.name}}"></div></div><h2>电视剧照片</h2><div class="form-group row"><label for="file" class="col-sm-2 col-form-label">照片</label><input type="file" name="file" id="file" class="form-control" #fileInput (change)="upload()"></div><div class="progress" *ngIf="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="0" aria-valuemin="0"aria-valuemax="100" [style.width]="progress"></div></div><div><img [src]="'http://localhost:5000/Uploads/' + p.fileName" [alt]="p.fileName" *ngFor="let p of photoes" class="m-1" width="200"height="200" /></div> </form>
打开tv-show-detail.component.ts:
import { Component, OnInit, ElementRef, ViewChild } from '@angular/core'; import { TvShowService } from '../../services/tv-show.service'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { TvShow } from '../../models/tv-show'; import { Subscription } from 'rxjs/Subscription'; import { ToastrService } from 'ngx-toastr'; import { PhotoService } from '../../services/photo.service'; import { Photo } from '../../models/photo'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/forkJoin'; import { HttpEvent, HttpEventType } from '@angular/common/http'; import { HttpResponse } from 'selenium-webdriver/http';@Component({selector: 'app-tv-show-detail',templateUrl: './tv-show-detail.component.html',styleUrls: ['./tv-show-detail.component.css'] }) export class TvShowDetailComponent implements OnInit {tvShowId: number;@ViewChild('fileInput') fileInput: ElementRef;model: TvShow = new TvShow();busy: Subscription;photoes: Photo[] = [];progress: string;constructor(private tvShowService: TvShowService,private router: Router,private route: ActivatedRoute,private toastr: ToastrService,private photoService: PhotoService) { }ngOnInit() {this.route.paramMap.switchMap((params: ParamMap) => {this.tvShowId = +params.get('id');return Observable.forkJoin<TvShow, Photo[]>(this.tvShowService.getById(this.tvShowId),this.photoService.getPhotoes(this.tvShowId));}).subscribe(([tvShow, photoes]) => {this.model = tvShow;this.photoes = photoes;});}upload() {const ele = this.fileInput.nativeElement;const file = ele.files[0]; ele.value = ''; // 上传图片后,把input的值清空.this.photoService.upload(this.tvShowId, file).subscribe((event: HttpEvent<any>) => {switch (event.type) {case HttpEventType.Sent:console.log(`开始上传 "${file.name}", 大小是: ${file.size}.`);break;case HttpEventType.UploadProgress:const percentDone = Math.round(100 * event.loaded / event.total);this.progress = `${percentDone}%`;console.log(`文件 "${file.name}" 的上传进度是 ${percentDone}%.`);break;case HttpEventType.Response:console.log(`文件 "${file.name}" 上传成功!`);this.toastr.success(`文件 "${file.name}" 上传成功!`);this.photoes.push(<Photo>(event.body));this.progress = null;break;default:console.log(`文件 "${file.name}" 的事件类型: ${event.type}.`);break;}});} }
试试效果:
OK, 没问题!
今天就写到这吧.
转载于:https://www.cnblogs.com/cgzl/p/8537366.html
用VSCode开发一个asp.net core2.0+angular5项目(5): Angular5+asp.net core 2.0 web api文件上传...相关推荐
- Asp.Net Core Web Api图片上传及MongoDB存储实例教程(一)
图片或者文件上传相信大家在开发中应该都会用到吧,有的时候还要对图片生成缩略图.那么如何在Asp.Net Core Web Api实现图片上传存储以及生成缩略图呢?今天我就使用MongoDB作为图片存储 ...
- Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程
Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程 原文:Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程 Asp.Net Cor ...
- Web安全 文件上传漏洞的 测试和利用.(上传一个图片或文件 拿下服务器最高权限.)
文件上传漏洞的概括 现在大多的网站和Web应用系统都会有上传功能(比如:文档,图片,头像,视频上传等.),而程序员在开发文件上传功能时,没有对代码做严格校验上传文件的后缀和文件类型. 此时攻击者就可以 ...
- 00截断上传绕过_【文件上传与解析】文件上传与解析漏洞总结v1.0
点击上方"公众号" 可以订阅哦! Hello,各位小伙伴晚上好~ 这里是依旧勤劳写公众号的小编~ 今天本公众号将推出一个新的模块,那就是漏洞知识点总结模块!!!(此处应有掌声~) ...
- asp.net 文件上传和下载管理源码
利用asp.net进行文件上传和下载时非常常用的功能,现做整理,将源码上传,提供给初学者参考,以下代码中的样式文件就不上传了,下载者请将样式去掉. 效果图如下: <%@ Page Languag ...
- Asp.net2.0下的大文件上传服务器控件
最近本来准备在Alisoft上做点东西玩,做的过程中,涉及到了文件上传.刚开始,准备在网上找几个用,发现国外好用的要钱,而国内的,开源的又不好用,所以决定自己也写一个,顺便以后赚点外快.我看了一下,这 ...
- 本周ASP.NET英文技术文章推荐[09/30- 07/13]:.NET Framework、JSON、Google Analytics、文件上传、GridView、IIS 7、Web开发...
摘要 本期共有9篇文章: .NET Framework源代发发布 Tip/Trick:在.NET 3.5中编写ToJSON扩展方法 在Google Analytics中统计访客浏览器的Silverli ...
- ASP.NET Core 3.1 Web API和EF Core 5.0 中具有泛型存储库和UoW模式的域驱动设计实现方法
目录 介绍 背景 领域驱动设计 存储库模式 工作单元模式 使用代码 创建空白解决方案和解决方案架构 添加和实现应用程序共享内核库 PageParam.cs 在Entity Framework Core ...
- iOS开发之结合asp.net webservice实现文件上传下载
iOS开发中会经常用到文件上传下载的功能,这篇文件将介绍一下使用asp.net webservice实现文件上传下载. 首先,让我们看下文件下载. 这里我们下载cnblogs上的一个zip文件.使用N ...
最新文章
- 浅析「扣减库存」的方案设计
- UIProgressView(进度条控件)
- python 打包图标_Python打包成exe文件很难?一分钟即可学会,并添加图标!
- mysql主从配置常见问题_mysql 主从复制配置,以及常见问题解决!
- c语言下面程序的功能是求圆的周长和面积.请改正程序中带*行中,2012年计算机等级考试二级C语言上机题(5)...
- 前端的UI设计与交互之反馈示篇
- OpenCV-怀旧色滤镜
- C# 递归实现tree view(树结构)
- 交换机(防火墙)配置手册
- php 怎么使用sql server 2000,Linux下PHP支持MSSQL(SQL Server2000)
- 直播讲座:时序数据库 Apache IoTDB 的核心技术与应用
- android模拟程序被杀死,Android模拟后台进程被杀
- 2020年机修钳工(初级)报名考试及机修钳工(初级)模拟试题
- 常用网络图片url地址
- Towards Robust Vision Transformer论文学习(CVPR2022)
- BigDecimal 往左移动两位小数_人教版小学数学四年级下册 小数点位置移动引起小数大小的变化 教案、课件,公开课视频...
- android 电视盒子项目外包
- 【2017】法定各种假期的规则及概念
- 微信消费券项目(开发以及流程完善版本)
- 服务器驱动用什么工具_驱动、改向滚筒用什么胶板进行包胶?
热门文章
- 南邮linux期末考试试题,南邮操作系统试卷及答案.doc
- 更灵活、有个性的卷积——可变形卷积(Deformable Conv)
- delphi 中如果不进行 closehandle 会怎么样_报考八一农大的十一大选择之七|带你走进不一样的“信息世界”...
- Android 日志自动分析,Android Log Viewer:一个日志查看器工具,可简化实时对Android日志的分析...
- 聊天机器人-AIML人工智能标记语言
- Android控件Gallery3D效果 .
- 【单片机实验】矩阵键盘
- Spring Cloud Greenwich 新特性和F升级分享
- Anaconda jupyter-notebook 添加kernel
- iOS解决表格中TextField,TextView编辑时,输入框被键盘遮挡的问题