首页 > 其他分享 >基于.NetCore开发博客项目 StarBlog - (25) 图片接口与文件上传

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

时间:2022-12-22 23:44:27浏览次数:74  
标签:25 基于 NetCore 博客 开发 StarBlog 图片

前言

上传文件的接口设计有两种风格,一种是整个项目只设置一个接口用来上传,然后其他需要用到文件的地方,都只存一个引用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);
}

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

系列文章

标签:25,基于,NetCore,博客,开发,StarBlog,图片
From: https://www.cnblogs.com/deali/p/16999818.html

相关文章

  • BZOJ 3252 攻略(长链剖分模板 链的常用性质 贪心)
    BZOJ3252攻略​ 给定一棵带边权的树,选择k个叶子结点,使这些叶子结点与根节点相连,形成k条路径。输出被路径覆盖的所有边的边权和的最大值(同一条边若被重复覆盖只算一......
  • 有了这25个正则表达式,代码效率提高80%
    本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。前言大家好,我是林三心,在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一......
  • luoguP2254 [NOI2005]瑰丽华尔兹 题解
    传送门题意给定\(n\)行\(m\)列的矩阵和钢琴的初始位置\((x,y)\),给定\(k\)个时间段,在每个时间段内钢琴会向给定方向(上/下/左/右)滑动,\(1\)秒移动\(1\)个单位长度......
  • 明德扬基于XILINX K7核心板325T/410T
    1.1产品简介MP5650核心板采用Xilinx公司Kintex-7系列的XC7K325T-2FFG900I/XC7K410T-2FFG900I作为主控制器,核心板采用4个0.5mm间距120Pin镀金连接器与母板连接,核心板四......
  • 为了实现2025年汽车业务盈利,华为准备复制几个问界
    车企准备好交出灵魂了吗?据《晚点Auto》报道,在今年12月8日的华为内部会议上,消费者BGCEO、智能汽车解决方案BUCEO余承东表示,车BU要在2025年实现盈利。......
  • POJ-2533 Longest Ordered Subsequence
    POJ-2533LongestOrderedSubsequence题意:给出一个序列,求出这个序列的最长上升子序列序列\(A\)的上升子序列\(B\)定义如下:\(B\)为\(A\)的子序列\(B\)为严......
  • [CF1325E] Ehab's REAL Number Theory Problem
    Ehab'sREALNumberTheoryProblem题目描述Youaregivenanarray$a$oflength$n$thathasaspecialcondition:everyelementinthisarrayhasatmost7......
  • Day25.2.Arrays类
    Day25.2.Arrays类1.介绍数组工具类java.util.Arrays由于数组对象本身并没有什么方法可以调用,但API中提供了工具类Arrays供使用,从而对数组对象进行一些基本操作具......
  • UVA 725 Division
    原题Vjudge题目大意给你个\(N\)判断有没有两个整数满足\(\frac{A}{B}=N\),并且\(A和B\)的各位数字刚好构成\(0\sim9\)的一个排列解题思路这题乍一看挺难的,但是范围很......
  • NetCore+Python实现视频上传mediapipe骨骼标注
     打开网页,选择视频,上传视频,解析完成后播放及视频下载   usingMicrosoft.AspNetCore.Hosting;usingMicrosoft.AspNetCore.Http;usingMicrosoft.AspNetCore.Mvc......