在b站学习了一个后端小项目,然后发现这个文件上传设计还挺不错,来实现讲解一下。
项目结构如下:
(资料图片)
基于策略+工厂模式实现文件上传服务
在Model层创建即可
public enum UploadMode { Local = 1, //本地上传 Qiniu = 2 //七牛云上传 }
在Service层中创建FileStrategy文件夹,在该文件夹下分别创建Strategy
、QiNiuStrategy
、LocalStrategy
、FileFactory
、FileContext
类
/// /// 文件操作抽象类/// public abstract class Strategy{ public abstract Task Upload(List formFiles);}
继承Strategy
文件操作抽象类并实现他里面的方法
后续要实现该方法
public class LocalStrategy:Strategy{ public override async Task Upload(List formFiles) { throw new NotImplementedException(); }}
同上
public class QiNiuStrategy:Strategy{ public override async Task Upload(List formFiles) { throw new NotImplementedException(); }}
在策略模式中通过上下文调用具体的策略
这里的好处就是我如果new LocalStrategy
则是本地上传服务,如果是new QiNiuStrategy
则是七牛云上传,详情看下方工厂的设计
/// /// 上下文,通过这个来调用具体的策略/// public class FileContext{ private Strategy _strategy; private List _formFiles; public FileContext(Strategy strategy, List formFiles) { _formFiles = formFiles; _strategy = strategy; } public async Task ContextInterface() { return await _strategy.Upload(_formFiles); }}
通过工厂去负责对象的实例化
枚举的作用就来了,更据枚举去判断实例化那一个对象
/// /// 工厂类,负责对象的实例化/// public class FileFactory{ public static Strategy CreateStrategy(UploadMode mode) { switch (mode) { case UploadMode.Qiniu: return new QiNiuStrategy(); case UploadMode.Local: return new LocalStrategy(); default: return new QiNiuStrategy(); } }}
在Interface接口层创建IFileService接口
这里需要安装Http
Nuget包,不然没有IFormFile
,我下的是Microsoft.AspNetCore.Http/2.2.2
public interface IFileService{ Task Upload(List files, UploadMode mode);}
在Service层创建FileService类实现上面的接口
public class FileService:IFileService{ public async Task Upload(List files, UploadMode mode) { FileContext fileContext = new FileContext(FileFactory.CreateStrategy(mode), files); return await fileContext.ContextInterface(); }}
上述通过上下文调用具体的策略,通过工厂去创建具体的类,工厂更据传入的枚举作为参数(构造函数传参),通过上下文的ContextInterface
就可以完成上传的逻辑了。
策略+工厂的好处就是,以后需要修改上传文件,只需要对策略进行更改和补充即可。意思就是新增一个上传策略,只需要创建一个策略类,然后去工厂类new一个就行了,不需要动Service层的东西。
在本地策略类中实现上传方法
注意var filePath = $"{AppContext.BaseDirectory}/wwwroot";
是将文件保存在bin目录下的wwwroot目录了
public class LocalStrategy:Strategy{ public override async Task Upload(List formFiles) { var res = await Task.Run(() => { //存放多个文件路径 List result = new List(); foreach (var formFile in formFiles) { if (formFile.Length > 0) { var filePath = $"{AppContext.BaseDirectory}/wwwroot"; var fileName = $"/{DateTime.Now:yyyyMMddHHmmssffff}{formFile.FileName}"; if (!Directory.Exists(filePath)) { Directory.CreateDirectory(filePath); } using (var stream = System.IO.File.Create(filePath + fileName)) { formFile.CopyTo(stream); } result.Add(fileName); } } return string.Join(",", result); }); return res; }}
注册七牛云:https://www.qiniu.com/
从个人中心获取秘钥信息,安装SDK,编写上传逻辑
在Service层安装Qiniu
Nuget包
ak、sk为七牛云的秘钥,在个人中心查看
public class QiNiuStrategy : Strategy{ public override async Task Upload(List formFiles) { //先上传到本地,才能上传到七牛云,上传完成后本地的文件可删除 var res = await Task.Run(() => { Mac mac = new Mac("ak", "sk"); List result = new List(); foreach (var formFile in formFiles) { if (formFile.Length > 0) { var filePath_temp = $"{AppContext.BaseDirectory}/Images_temp"; var fileName = $"{DateTime.Now:yyyyMMddHHmmssffff}{formFile.FileName}"; if (!Directory.Exists(filePath_temp)) { Directory.CreateDirectory(filePath_temp); } using (var stream = System.IO.File.Create($"{filePath_temp}/{fileName}")) { formFile.CopyTo(stream); } // 上传文件名 string key = fileName; // 本地文件路径 string filePath = $"{filePath_temp}/{fileName}"; // 存储空间名 string Bucket = "pl-static"; // 设置上传策略 PutPolicy putPolicy = new PutPolicy(); // 设置要上传的目标空间 putPolicy.Scope = Bucket; // 上传策略的过期时间(单位:秒) //putPolicy.SetExpires(3600); // 文件上传完毕后,在多少天后自动被删除 //putPolicy.DeleteAfterDays = 1; // 生成上传token string token = Auth.CreateUploadToken(mac, putPolicy.ToJsonString()); Config config = new Config(); // 设置上传区域 config.Zone = Zone.ZONE_CN_East; // 设置 http 或者 https 上传 config.UseHttps = true; config.UseCdnDomains = true; config.ChunkSize = ChunkUnit.U512K; // 表单上传 FormUploader target = new FormUploader(config); HttpResult httpResult = target.UploadFile(filePath, key, token, null); result.Add(fileName); //删除备份文件夹 Directory.Delete(filePath_temp, true); } } return string.Join(",", result); }); return res; }}
mode为1则会执行本地上传的逻辑代码,为2则会执行七牛云上传的服务代码。
[Route("api/[controller]/[action]")][ApiController]public class FileController : ControllerBase{ private readonly IFileService _fileService; public FileController(IFileService fileService) { _fileService = fileService; } /// /// 上传文件 /// /// 文件对象 /// 上传方式:本地或者七牛云 /// [HttpPost] public async Task UploadFile(List file, UploadMode mode) { return ResultHelper.Success(await _fileService.Upload(file, mode)); }}
返回的图片路径可以根据自己的需求进行更改。
去bin目录查看图片是否上传成功
上述内容就是对于文件上传服务的设计,有其他文件上传需求,比如分片断点上传、上传到其他的服务商,只需要新增策略,完成逻辑代码即可,还是很方便的一种文件上传设计。
关键词: