前言

上传文件的接口设计有两种风格,一种是整个项目只设置一个接口用来上传,然后其他需要用到文件的地方,都只存一个引用ID;另一种是每个需要文件的地方单独管理各自的文件。这俩各有优劣吧,本项目中选择的是后者的风格,文章图片和照片模块又要能CRUD又要批量导入,还是各自管理文件比较好。

图片接口

说会正题,先介绍一下图片相关接口。

图片列表

首先CRUD是肯定有的,图片列表的分页查看也是有的,不过因为筛选功能没有做,所以就不定义一个ViewModel作为参数了。

控制器代码 StarBlog.Web/Apis/Blog/PhotoController.cs

[HttpGet]
public ApiResponsePaged<Photo> GetList(int page = 1, int pageSize = 10) {
var paged = _photoService.GetPagedList(page, pageSize);
return new ApiResponsePaged<Photo> {
Pagination = paged.ToPaginationMetadata(),
Data = paged.ToList()
};
}

跟博客前台公用一套图片列表逻辑,所以这部分抽出来放在service,代码如下

StarBlog.Web/Services/PhotoService.cs

public IPagedList<Photo> GetPagedList(int page = 1, int pageSize = 10) {
return _photoRepo.Select.OrderByDescending(a => a.CreateTime)
.ToList().ToPagedList(page, pageSize);
}

单个图片

获取单个图片,跟获取文章的差不多,传入ID,找不到就返回404,找到就返回图片对象

[HttpGet("{id}")]
public ApiResponse<Photo> Get(string id) {
var photo = _photoService.GetById(id);
return photo == null
? ApiResponse.NotFound($"图片 {id} 不存在")
: new ApiResponse<Photo> {Data = photo};
}

图片缩略图

在本系列第20篇中,本项目已经实现了图片显示的优化,详见:基于.NetCore开发博客项目 StarBlog - (20) 图片显示优化

除了 ImageSharp 组件提供的图片缩略图功能外,我这里还写了另一个生成缩略图的方法,这个方法有俩特点

  • 直接在内存中生成返回,不会写入缓存文件
  • 生成的是Progressive JPEG格式,目前 ImageSharp 是不支持的,可以优化前端的加载速度

控制器代码

[HttpGet("{id}/Thumb")]
public async Task<IActionResult> GetThumb(string id, [FromQuery] int width = 300) {
var data = await _photoService.GetThumb(id, width);
return new FileContentResult(data, "image/jpeg");
}

service代码

/// <summary>
/// 生成Progressive JPEG缩略图 (使用 MagickImage)
/// </summary>
/// <param name="width">设置为0则不调整大小</param>
public async Task<byte[]> GetThumb(string id, int width = 0) {
var photo = await _photoRepo.Where(a => a.Id == id).FirstAsync();
using (var image = new MagickImage(GetPhotoFilePath(photo))) {
image.Format = MagickFormat.Pjpeg;
if (width != 0) {
image.Resize(width, 0);
} return image.ToByteArray();
}
}

这个 MagickImage 是用 C++ 写的,在不同平台上引用不同 native 库,需要在 csproj 里面写上配置,这样发布的时候才会带上对应的依赖库,而且似乎在 CentOS 系统上会有坑…

<!--  复制 Magick 库  -->
<PropertyGroup>
<MagickCopyNativeWindows>true</MagickCopyNativeWindows>
<MagickCopyNativeLinux>true</MagickCopyNativeLinux>
<MagickCopyNativeMacOS>true</MagickCopyNativeMacOS>
</PropertyGroup>

其他接口

还有一些接口,跟之前介绍的大同小异,再重复一次也意义不大,读者有需要的话可以自行查看源码。

图片文件上传

这个同时也是图片的添加接口

先定义DTO

public class PhotoCreationDto {
/// <summary>
/// 作品标题
/// </summary>
[Required(ErrorMessage = "作品标题不能为空")]
public string Title { get; set; } /// <summary>
/// 拍摄地点
/// </summary>
[Required(ErrorMessage = "拍摄地点不能为空")]
public string Location { get; set; }
}

控制器代码

[Authorize]
[HttpPost]
public ApiResponse<Photo> Add([FromForm] PhotoCreationDto dto, IFormFile file) {
var photo = _photoService.Add(dto, file); return !ModelState.IsValid
? ApiResponse.BadRequest(ModelState)
: new ApiResponse<Photo>(photo);
}

因为上传的同时还要附带一些数据,需要使用 FormData 传参,所以这里使用 [FromForm] 特性标记这个 dto 参数

IFormFile 类型的参数可以拿到上传上来的文件

下面是service代码

public Photo Add(PhotoCreationDto dto, IFormFile photoFile) {
var photoId = GuidUtils.GuidTo16String();
var photo = new Photo {
Id = photoId,
Title = dto.Title,
CreateTime = DateTime.Now,
Location = dto.Location,
FilePath = Path.Combine("photography", $"{photoId}.jpg")
}; var savePath = Path.Combine(_environment.WebRootPath, "media", photo.FilePath); // 如果超出最大允许的大小,则按比例缩小
const int maxWidth = 2000;
const int maxHeight = 2000;
using (var image = Image.Load(photoFile.OpenReadStream())) {
if (image.Width > maxWidth)
image.Mutate(a => a.Resize(maxWidth, 0));
if (image.Height > maxHeight)
image.Mutate(a => a.Resize(0, maxHeight));
image.Save(savePath);
} // 保存文件
using (var fs = new FileStream(savePath, FileMode.Create)) {
photoFile.CopyTo(fs);
} // 读取图片的尺寸等数据
photo = BuildPhotoData(photo); return _photoRepo.Insert(photo);
}

这里对图片做了一些处理,抛开这些细节,其实对上传的文件,最关键的只有几行保存代码

using (var fs = new FileStream("savePath", FileMode.Create)) {
photoFile.CopyTo(fs);
}

这样就完成了文件上传接口。

系列文章

基于.NetCore开发博客项目 StarBlog - (25) 图片接口与文件上传的更多相关文章

  1. 基于.NetCore开发博客项目 StarBlog - (10) 图片瀑布流

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  2. 基于.NetCore开发博客项目 StarBlog - (9) 图片批量导入

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  3. 基于.NetCore开发博客项目 StarBlog - (11) 实现访问统计

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  4. 基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  5. 基于.NetCore开发博客项目 StarBlog - (13) 加入友情链接功能

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  6. 基于.NetCore开发博客项目 StarBlog - (14) 实现主题切换功能

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. 基于.NetCore开发博客项目 StarBlog - (15) 生成随机尺寸图片

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  8. 基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  9. 基于.NetCore开发博客项目 StarBlog - (17) 自动下载文章里的外部图片

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  10. 基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传

    前言 九月太忙,只更新了三篇文章,本来这个功能是从九月初就开始做的,结果一直拖到现在国庆假期才有时间完善并且写文章~ 之前我更新了几篇关于 Python 的文章,有朋友留言问是不是不更新 .Net 了 ...

随机推荐

  1. Ubuntu转到root用户后用户名路径没有颜色

    看到好多都直接把普通会用户的.bashrc配置复制到root家目录下,由于本人小白,很多.bashrc配置看不懂,也不敢随便修改.于是找到别的配置方法. 普通用户的os@a:~$和root用户的roo ...

  2. .Net CLR GC plan_phase二叉树和Brick_table

    楔子 别那么懒,勤快点.以下取自CLR PreView 7.0. 主题 GC计划阶段(plan_phase)主要就两个部分,一个是堆里面的对象构建一颗二叉树(这颗二叉树的每个节点包含了诸如对象移动信息 ...

  3. GitLab + Jenkins + Harbor 工具链快速落地指南

    目录 一.今天想干啥? 二.今天干点啥? 三.今天怎么干? 3.1.常规打法 3.2.不走寻常路 四.开干吧! 4.1.工具链部署 4.2.网络配置 4.3.验证工具链部署结果 4.3.1.GitLa ...

  4. BigDecimal 用法总结

    转载请注明出处: 目录 1.BigDecimal 简介 2.构造BigDecimal的对象 3.常用方法总结 4.divide方法使用 5.setScale 方法使用 6.BigDecimal 数据库 ...

  5. JVM中的堆

    堆 内存结构 堆的核心概念 <java虚拟机规范>中对java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上. 一个JVM实例只存在一个堆内存(就是new 出来一个对象),ja ...

  6. 说说 Redis pipeline

    更多技术文章,请关注我的个人博客 www.immaxfang.com 和小公众号 Max的学习札记. Redis 客户端和服务端之间是采用 TCP 协议进行通信的,是基于 Request/Respon ...

  7. LcdTools如何导出内置画面为bmp图片

    运行LcdTools,先设置好图片所需分辨率参数,点击"画面设置"栏,修改下图所示参数 点击"画面设置"栏,在"画面资源"栏找到需要导出的画 ...

  8. 2.httprunner-yaml用例结构

    前言: httprunner3.x版本弱化了api层的概念 直接在testcase中写request请求 如果是单个请求,也可以直接写成一个testcase 每个testcase必须具有两个类属性:c ...

  9. Windows7下驱动开发与调试体系构建——4.在x64下使用汇编代码(x86下的_asm)

    目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html asm文件设置 在vs x64中无法使用_asm关键字,需要使用.asm文件. 按第 ...

  10. 论文笔记 - PRISM: A Rich Class of Parameterized Submodular Information Measures for Guided Subset Selection

    Motivation 与 Active Learning 类似,Target Learning 致力于 挑选外卖更"感兴趣"的数据,即人为为更重要的数据添加 bias.例如我们当前 ...