前言
上傳檔案的介面設計有兩種風格,一種是整個專案只設定一個介面用來上傳,然後其他需要用到檔案的地方,都只存一個引用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>
其他介面
還有一些介面,跟之前介紹的大同小異,再重複一次也意義不大,讀者有需要的話可以自行檢視原始碼。
- 刪除圖片
- 設定和取消推薦
- 重建圖片庫資料(更新每個圖片的尺寸等資料,一般情況下不需要用到)
- 批次匯入(本系列的第9篇已經介紹過)詳見:基於.NetCore開發部落格專案 StarBlog - (9) 圖片批次匯入
圖片檔案上傳
這個同時也是圖片的新增介面
先定義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 - (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) 圖片介面與檔案上傳