第一部分: 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文件上传...相关推荐

  1. Asp.Net Core Web Api图片上传及MongoDB存储实例教程(一)

    图片或者文件上传相信大家在开发中应该都会用到吧,有的时候还要对图片生成缩略图.那么如何在Asp.Net Core Web Api实现图片上传存储以及生成缩略图呢?今天我就使用MongoDB作为图片存储 ...

  2. Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程

    Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程 原文:Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程 Asp.Net Cor ...

  3. Web安全 文件上传漏洞的 测试和利用.(上传一个图片或文件 拿下服务器最高权限.)

    文件上传漏洞的概括 现在大多的网站和Web应用系统都会有上传功能(比如:文档,图片,头像,视频上传等.),而程序员在开发文件上传功能时,没有对代码做严格校验上传文件的后缀和文件类型. 此时攻击者就可以 ...

  4. 00截断上传绕过_【文件上传与解析】文件上传与解析漏洞总结v1.0

    点击上方"公众号" 可以订阅哦! Hello,各位小伙伴晚上好~ 这里是依旧勤劳写公众号的小编~ 今天本公众号将推出一个新的模块,那就是漏洞知识点总结模块!!!(此处应有掌声~) ...

  5. asp.net 文件上传和下载管理源码

    利用asp.net进行文件上传和下载时非常常用的功能,现做整理,将源码上传,提供给初学者参考,以下代码中的样式文件就不上传了,下载者请将样式去掉. 效果图如下: <%@ Page Languag ...

  6. Asp.net2.0下的大文件上传服务器控件

    最近本来准备在Alisoft上做点东西玩,做的过程中,涉及到了文件上传.刚开始,准备在网上找几个用,发现国外好用的要钱,而国内的,开源的又不好用,所以决定自己也写一个,顺便以后赚点外快.我看了一下,这 ...

  7. 本周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 ...

  8. ASP.NET Core 3.1 Web API和EF Core 5.0 中具有泛型存储库和UoW模式的域驱动设计实现方法

    目录 介绍 背景 领域驱动设计 存储库模式 工作单元模式 使用代码 创建空白解决方案和解决方案架构 添加和实现应用程序共享内核库 PageParam.cs 在Entity Framework Core ...

  9. iOS开发之结合asp.net webservice实现文件上传下载

    iOS开发中会经常用到文件上传下载的功能,这篇文件将介绍一下使用asp.net webservice实现文件上传下载. 首先,让我们看下文件下载. 这里我们下载cnblogs上的一个zip文件.使用N ...

最新文章

  1. 浅析「扣减库存」的方案设计
  2. UIProgressView(进度条控件)
  3. python 打包图标_Python打包成exe文件很难?一分钟即可学会,并添加图标!
  4. mysql主从配置常见问题_mysql 主从复制配置,以及常见问题解决!
  5. c语言下面程序的功能是求圆的周长和面积.请改正程序中带*行中,2012年计算机等级考试二级C语言上机题(5)...
  6. 前端的UI设计与交互之反馈示篇
  7. OpenCV-怀旧色滤镜
  8. C# 递归实现tree view(树结构)
  9. 交换机(防火墙)配置手册
  10. php 怎么使用sql server 2000,Linux下PHP支持MSSQL(SQL Server2000)
  11. 直播讲座:时序数据库 Apache IoTDB 的核心技术与应用
  12. android模拟程序被杀死,Android模拟后台进程被杀
  13. 2020年机修钳工(初级)报名考试及机修钳工(初级)模拟试题
  14. 常用网络图片url地址
  15. Towards Robust Vision Transformer论文学习(CVPR2022)
  16. BigDecimal 往左移动两位小数_人教版小学数学四年级下册 小数点位置移动引起小数大小的变化 教案、课件,公开课视频...
  17. android 电视盒子项目外包
  18. 【2017】法定各种假期的规则及概念
  19. 微信消费券项目(开发以及流程完善版本)
  20. 服务器驱动用什么工具_驱动、改向滚筒用什么胶板进行包胶?

热门文章

  1. 南邮linux期末考试试题,南邮操作系统试卷及答案.doc
  2. 更灵活、有个性的卷积——可变形卷积(Deformable Conv)
  3. delphi 中如果不进行 closehandle 会怎么样_报考八一农大的十一大选择之七|带你走进不一样的“信息世界”...
  4. Android 日志自动分析,Android Log Viewer:一个日志查看器工具,可简化实时对Android日志的分析...
  5. 聊天机器人-AIML人工智能标记语言
  6. Android控件Gallery3D效果 .
  7. 【单片机实验】矩阵键盘
  8. Spring Cloud Greenwich 新特性和F升级分享
  9. Anaconda jupyter-notebook 添加kernel
  10. iOS解决表格中TextField,TextView编辑时,输入框被键盘遮挡的问题