基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传
在基于SqlSugar的开发框架的服务层中处理文件上传的时候,我们一般有两种处理方式,一种是常规的把文件存储在本地文件系统中,一种是通过FTP方式存储到指定的FTP服务器上。这种处理应该由程序进行配置,决定使用那种方式,那么这里面我们为了弹性化处理, 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传的配置参数信息。
微软引入选项模式,它是用于配置框架服务使用的设置. 选项模式由Microsoft.Extensions.OptionsNuGet包实现,除了ASP.NET Core应用,它还适用于任何类型的应用程序,如果需要了解,微软的文档详细解释了选项模式。
选项模式的限制之一是你只能解析(注入) IOptions <MyOptions>
并在依赖注入配置完成(即所有模块的ConfigureServices
方法完成)后获取选项值。如果你正在开发一个模块,可能需要让开发者能够设置一些选项,并在依赖注入注册阶段使用这些选项. 你可能需要根据选项值配置其他服务或更改依赖注入的注册代码.
1、文件上传处理
在前面我们介绍过,文件上传处理的逻辑中有两部分,一个是本地文件处理,一个是FTP文件处理,它们选择那种方式,依赖于配置参数的信息,如下示意图所示。
在本地文件处理过程中,如果是Web API方式调用服务层,那么就在Web API所在的文件系统中,如果是Winform界面直接调用服务层,那么就是在当前系统中处理文件,这种方式可以有效的管理我们的文件信息。
在FTP文件处理过程中,则是根据选项参数的信息,调用FluentFTP类库进行文件的上传操作。
在Winform界面中上传文件的界面如下所示,它对于采用哪种方式是不知道的,具体由配置参数决定。
而所有的附件信息我们存储在数据库里面,文件则存放在对应的文件夹里面,可以统一进行管理查看和管理。
2、选项模式【Options】的处理
首先根据我们的处理方式,我们定义一个对象,用于承载上传参数的信息,如下代码所示。
/// <summary>
/// 文件上传处理的选项信息
/// </summary>
public class UploadSettingOptions
{
/// <summary>
/// 可指定的存储物理地址,如C:\\Attachment,如果没有配置项AttachmentBasePath,则默认一个相对目录。
/// </summary>
public string AttachmentBasePath { get; set; } /// <summary>
/// 指定附件上传的方式,如ftp为FTP方式,为空则为普通方式
/// </summary>
public string AttachmentUploadType { get; set; } /// <summary>
/// FTP服务地址
/// </summary>
public string FtpServer { get; set; } /// <summary>
/// FTP用户名
/// </summary>
public string FtpUser { get; set; } /// <summary>
/// FTP密码
/// </summary>
public string FtpPassword { get; set; } /// <summary>
/// FTP的基础路径,如可以指定为IIS的路径:http://www.iqidi.com:8000 ,方便下载打开
/// </summary>
public string FtpBaseUrl { get; set; }
}
然后在项目中添加Microsoft.Extensions.Options引用。
我们定义文件上传服务类的和它的构造函数,以便于选项模式的处理。
/// <summary>
/// 上传附件信息 应用层服务接口实现
/// </summary>
public class FileUploadService : MyCrudService<FileUploadInfo,string, FileUploadPagedDto>, IFileUploadService
{
//微软引入选项模式,它是用于配置框架服务使用的设置. 选项模式由Microsoft.Extensions.Options NuGet包实现
//在你需要获得一个选项值时,将 IOptions<TOption> 服务注入到你的类中,使用它的 .Value 属性得到值.
private readonly UploadSettingOptions _options; /// <summary>
/// 参数化构造,注入上传处理设置信息
/// </summary>
/// <param name="options"></param>
public FileUploadService(IOptions<UploadSettingOptions> options)
{
_options = options.Value;
}
我们看到这里提供了一个注入接口的参数信息,这样完成参数的注入加载后,我们在该服务类调用的时候,就可以使用它的选项参数信息了。
例如我们在其中通用的上传处理方法上如下所示。
/// <summary>
/// 上传文件(根据配置文件选择合适的上传方式)
/// </summary>
/// <param name="info">文件信息(包含流数据)</param>
/// <returns></returns>
public async Task<CommonResult> Upload(FileUploadInfo info)
{
var uploadType = this._options.AttachmentUploadType; if (string.IsNullOrEmpty(uploadType))
{
return await this.UploadByNormal(info);
}
else if (uploadType.Equals("ftp", StringComparison.OrdinalIgnoreCase))
{
return await this.UploadByFTP(info);
}
else
{
throw new ArgumentException("AttachmentUploadType配置指定了无效的值, 请置空或者填写ftp。");
}
}
我们来通过读取选项参数的信息,来决定采用哪种上传文件的方式。
我们在基于.net framewrok的Winform项目App.config中指定下面的配置信息
<appSettings>
<!--保存目录及FTP配置信息-->
<!--可指定的存储物理地址,如C:\\Attachment,如果没有配置则默认相对目录。-->
<add key="AttachmentBasePath" value="" />
<!--指定附件默认上传的方式, 如为FTP方式:ftp,为空则为普通方式-->
<add key="AttachmentUploadType" value="" />
<!--非空的时候,读取下面的FTP信息-->
<add key="FtpServer" value="114.215.106.96" />
<add key="FtpUser" value="web2" />
<add key="FtpPassword" value="" />
<!--可以指定为HTTP或者FTP的路径,供下载查看-->
<add key="FtpBaseUrl" value="http://www.iqidi.com/ftp" />
</appSettings>
而在项目启动的时候,我们需要注入选项参数的内容,以达到完成配置文件信息的读取。
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
// IServiceCollection负责注册
IServiceCollection services = new ServiceCollection();//添加IApiUserSession实现类
services.AddSingleton<IApiUserSession, ApiUserPrincipal>(); //CurrentPrincipal实现方式 //构造对应环境下的UploadSettingOptions信息
services.Configure<UploadSettingOptions>(option =>
{
SetUploadOptions(option);
});
/// <summary>
/// 构造对应环境下的UploadSettingOptions信息
/// </summary>
/// <returns></returns>
private static UploadSettingOptions SetUploadOptions(UploadSettingOptions option)
{
var config = new AppConfig();
option.AttachmentBasePath = config.AppConfigGet("AttachmentBasePath");
option.AttachmentUploadType = config.AppConfigGet("AttachmentUploadType");
option.FtpServer = config.AppConfigGet("FtpServer");
option.FtpUser = config.AppConfigGet("FtpUser");
option.FtpPassword = config.AppConfigGet("FtpPassword");
option.FtpBaseUrl = config.AppConfigGet("FtpBaseUrl"); return option;
}
就这样,在.net framework的WInform项目启动的时候,我们就完成了文件上传选项参数的读取加载,这样在 FileUploadService 里面的构造函数,就可以获得对应的选项参数信息对象了。
对于.netcore的程序,我们知道它的配置信息是appSettings.json,如下节点所示。
"UploadSettingOptions": {
"AttachmentBasePath": "", //可指定的存储物理地址,如C:\\Attachment,如果没有配置则默认相对目录。
"AttachmentUploadType": "", //ftp或空,为空则为普通方式
"FtpServer": "114.215.106.96", //FTP服务器地址
"FtpUser": "web2", //FTP账号
"FtpPassword": "", //FTP密码
"FtpBaseUrl": "http://www.iqidi.com/ftp"//可以指定为HTTP或者FTP的路径,供下载查看
}
例如对于我们在Web API的初始化选项对象信息,具体加载的代码如下所示。
builder.Services.Configure<UploadSettingOptions>(builder.Configuration.GetSection("UploadSettingOptions"));
相对.net framework的处理来说,使用扩展函数来简化了不少。
这样,我们在文件上传处理的服务类中就可以顺利获得对应的配置信息了。
/// <summary>
/// 上传附件信息 应用层服务接口实现
/// </summary>
public class FileUploadService : MyCrudService<FileUploadInfo,string, FileUploadPagedDto>, IFileUploadService
{
//微软引入选项模式,它是用于配置框架服务使用的设置. 选项模式由Microsoft.Extensions.Options NuGet包实现
//在你需要获得一个选项值时,将 IOptions<TOption> 服务注入到你的类中,使用它的 .Value 属性得到值.
private readonly UploadSettingOptions _options; /// <summary>
/// 参数化构造,注入上传处理设置信息
/// </summary>
/// <param name="options"></param>
public FileUploadService(IOptions<UploadSettingOptions> options)
{
_options = options.Value;
}
我们在使用FTP上传文件的时候,使用了FluentFtp类库实现FTP文件的上传处理的,构建FTP对象的处理代码如下所示。
//使用FluentFTP操作FTP文件
var client = new FtpClient(this._options.FtpServer, this._options.FtpUser, this._options.FtpPassword); //如果配置指定了端口,则使用特定端口
if (!string.IsNullOrEmpty(this._options.FtpServer) && this._options.FtpServer.Contains(":"))
{
string port = this._options.FtpServer.Split(':')[1];
if (!string.IsNullOrEmpty(port))
{
client.Port = port.ToInt32();
}
}
上传FTP文件的代码如下所示。
//确定日期时间目录(格式:yyyy-MM),不存在则创建
string savePath = string.Format("/{0}-{1:D2}/{2}", DateTime.Now.Year, DateTime.Now.Month, category);
bool isExistDir = client.DirectoryExists(savePath);
if (!isExistDir)
{
client.CreateDirectory(savePath);
} //使用FTP上传文件
//避免文件重复,使用GUID命名
var ext = FileUtil.GetExtension(info.FileName);
var newFileName = string.Format("{0}{1}", Guid.NewGuid().ToString(), ext);//FileUtil.GetFileName(file); savePath = savePath.UriCombine(newFileName);
var uploaded = await client.UploadAsync(info.FileData, savePath, FtpRemoteExists.Overwrite, true); //成功后,写入数据库
if (uploaded == FtpStatus.Success)
{
通过选项参数的方式,我们可以配置参数的IOC处理话,从而实现了参数的灵活配置和读取操作。
在其他业务处理的服务类中,如果涉及到一些需要配置的信息,我们都可以利用这种模式实现参数内容的配置处理。
基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传的更多相关文章
- 基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理
在早期的随笔就介绍过,把常规页面的内容拆分为几个不同的组件,如普通的页面,包括列表查询.详细资料查看.新增资料.编辑资料.导入资料等页面场景,这些内容相对比较独立,而有一定的代码量,本篇随笔介绍基于V ...
- 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录
在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...
- 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用
在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...
- 基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用
刚完成一些前端项目的开发,腾出精力来总结一些前端开发的技术点,以及继续完善基于SqlSugar的开发框架循序渐进介绍的系列文章,本篇随笔主要介绍一下基于Vue3+TypeScript的全局对象的注入和 ...
- 基于SqlSugar的开发框架循序渐进介绍(15)-- 整合代码生成工具进行前端界面的生成
在前面随笔<基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理>中我们已经介绍过,对于相关的业务表的界面代码,我们已经尽可能把不同的业务逻辑 ...
- 基于SqlSugar的开发框架循序渐进介绍(17)-- 基于CSRedis实现缓存的处理
在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间 ...
- 基于SqlSugar的开发框架循序渐进介绍(20)-- 在基于UniApp+Vue的移动端实现多条件查询的处理
在做一些常规应用的时候,我们往往需要确定条件的内容,以便在后台进行区分的进行精确查询,在移动端,由于受限于屏幕界面的情况,一般会对多个指定的条件进行模糊的搜索,而这个搜索的处理,也是和前者强类型的条件 ...
- 基于SqlSugar的开发框架循序渐进介绍(21)-- 在工作流列表页面中增加一些转义信息的输出,在后端进行内容转换
有时候,为了给前端页面输出内容,有时候我们需要准备和数据库不一样的实体信息,因为数据库可能记录的是一些引用的ID或者特殊字符,那么我们为了避免前端单独的进行转义处理,我们可以在后端进行统一的格式化后再 ...
- 基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发
我喜欢在一个项目开发模式成熟的时候,使用代码生成工具Database2Sharp来配套相关的代码生成,对于我介绍的基于SqlSugar的开发框架,从整体架构确定下来后,我就着手为它们量身定做相关的代码 ...
随机推荐
- 基于nodejs中实现跨域的方法
一般情况下跨域是通过ajax的方式请求数据,通过js在不同的域之间进行数据传输或者通信: 只有通过ajax方式获取请求的时候才会有跨域问题需要解决: 例如在本地模拟两个服务端. 一个服务端去通过aja ...
- Java实现单链表的逆序打印
思路1:可以将链表进行反转,然后进行数据的输出即可,单链表反转地址如下https://blog.csdn.net/Kevinnsm/article/details/113763272 这个思路1肯定有 ...
- Java中List接口重要实现类一ArrayList
1.java.util.ArrayList 集合数据存储的结构是数组结构.元素查找快,而增删就比较慢 所以如果要查询数据,遍历数据,ArrayList是最常用的集合 2.ArrayList是不同步的, ...
- SpringBoot-总结
SpringBoot一站式开发 官网:https://spring.io/projects/spring-boot Spring Boot可以轻松创建独立的.基于Spring的生产级应用程序,它可以让 ...
- Python-初见
目录 概述 关键字 标准数据类型 Number String List Tuple Set Dictionary 删除对象 数据类型转换 推导式 运算符 迭代器与生成器 迭代器 生成器 函数 参数传递 ...
- 简简单单用一下 Hbase
一.Hbase 介绍 https://hbase.apache.org/book.html#_preface https://blogs.apache.org/hbase/ https://resea ...
- Unity-动画状态机使用细节记录
Unity动画控制器Animator功能非常强大,总结一些具体使用细节,在动作游戏中很实用: 1.动画烘焙 不同动画之间,可能存在角色朝向,重心高度不一致: 可以在动画Eidt界面设置RootTran ...
- 介绍一款倍受欢迎的.NET 开源UI库
概述 今天要带大家了解的是一款WPF的开源控件库MahApps.Metro.MahApps.Metro是用于创建现代WPF应用程序的工具包,它许多开箱即用的好东西. 目前支持的NET Framewor ...
- 【简单dfs】Bubble Cup 14 - Finals Online Mirror (Unrated, ICPC Rules, Teams Preferred, Div. 2), problem: (J) Robot Factory,
传送门 Problem - 1600J - Codeforces 题目 题意 给定n行m列, 求每个连通块由多少格子组成,并将格子数从大到小排序输出 对于每个格子都有一个数(0~15),将其转化 ...
- vue滚动分页加载
做了一个项目,要求将后台数据滚动加载. 滚动加载必须要求后台传的接口中由pageSize和pageIndex两个参数,来判断每次传数据的条数和数据的页码. 首先要判断滑轮是向上滚动还是向下滚动,可以在 ...