1前言
上传文件的接口设计有两种风格,一种是整个项目只设置一个接口用来上传,然后其他需要用到文件的地方,都只存一个引用ID;另一种是每个需要文件的地方单独管理各自的文件。这俩各有优劣吧,本项目中选择的是后者的风格,文章图片和照片模块又要能CRUD又要批量导入,还是各自管理文件比较好。
2图片接口
说会正题,先介绍一下图片相关接口。
图片列表
首先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>
其他接口
还有一些接口,跟之前介绍的大同小异,再重复一次也意义不大,读者有需要的话可以自行查看源码。
删除图片
设置和取消推荐
重建图片库数据(更新每个图片的尺寸等数据,一般情况下不需要用到)
批量导入(本系列的第9篇已经介绍过)详见:基于.NetCore开发博客项目 StarBlog - (9) 图片批量导入
3图片文件上传
这个同时也是图片的添加接口
先定义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);
}
这样就完成了文件上传接口。
4系列文章
基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客?
基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目
基于.NetCore开发博客项目 StarBlog - (3) 模型设计
基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入
基于.NetCore开发博客项目 StarBlog - (5) 开始搭建Web项目
基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表
基于.NetCore开发博客项目 StarBlog - (7) 页面开发之文章详情页面
基于.NetCore开发博客项目 StarBlog - (8) 分类层级结构展示
基于.NetCore开发博客项目 StarBlog - (9) 图片批量导入
基于.NetCore开发博客项目 StarBlog - (10) 图片瀑布流
基于.NetCore开发博客项目 StarBlog - (11) 实现访问统计
基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译
基于.NetCore开发博客项目 StarBlog - (13) 加入友情链接功能
基于.NetCore开发博客项目 StarBlog - (14) 实现主题切换功能
基于.NetCore开发博客项目 StarBlog - (15) 生成随机尺寸图片
基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)
基于.NetCore开发博客项目 StarBlog - (17) 自动下载文章里的外部图片
基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传
基于.NetCore开发博客项目 StarBlog - (19) Markdown渲染方案探索
基于.NetCore开发博客项目 StarBlog - (20) 图片显示优化
基于.NetCore开发博客项目 StarBlog - (21) 开始开发RESTFul接口
基于.NetCore开发博客项目 StarBlog - (22) 开发博客文章相关接口
基于.NetCore开发博客项目 StarBlog - (23) 文章列表接口分页、过滤、搜索、排序
基于.NetCore开发博客项目 StarBlog - (24) 统一接口数据返回格式
基于.NetCore开发博客项目 StarBlog - (25) 图片接口与文件上传