在平台開發中,我們往往對性能要求十分嚴苛,每一個字段、接口都有嚴格的要求。
系統中文件流操作十分占用資源,這里為大家介紹對文件上傳進行哈希校驗---同一文件只允許上傳一次到服務器,其他的上傳只要指向文件地址即可。
首先需要設計一張用於文件管理的業務表:
public class SysFile { public int Id { get; set; } /// <summary> /// 文件名稱 /// </summary> [StringLength(100)] public string FileName { get; set; } /// <summary> /// 文件存放路徑 /// </summary> [StringLength(200)] public string FilePath { get; set; } /// <summary> /// 存放目錄 /// </summary> [StringLength(100)] public string FileDirectory { get; set; } /// <summary> /// 文件大小 /// </summary> public int FileSize { get; set; } /// <summary> /// 哈希值 SHa256 /// </summary> [StringLength(100)] public string Hash { get; set; } /// <summary> /// 關聯表 /// </summary> public string LinkName { get; set; } /// <summary> /// 關聯ID /// </summary> public int LinkID { get; set; } }
實際項目中可以根據該表寫一個領域服務,方便接口調用。
由於需要驗證文件是否存在,所以需要提供查詢和上傳接口:
public async Task<List<UploadFileDto>> Check(List<UploadFileDto> input) { var existFiles = await _fileRepository.GetAllListAsync(_ => input.Select(f => f.FileHash).Contains(_.Hash)); foreach (var file in input) { var existFile = existFiles.FirstOrDefault(_ => _.Hash == file.FileHash); if(existFile!=null) { input.Remove(file); } } return input; } public async Task<bool> Upload(IFormFileCollection files) { if (!files.Any()) return false; var filePath = Path.Combine(_env.ContentRootPath, "wwwroot/Upload"); if (!Directory.Exists(filePath)) Directory.CreateDirectory(filePath); foreach (var file in files) { var fileName = $"{Guid.NewGuid()}@{file.FileName}"; var tempFilePath = Path.Combine(filePath, fileName); using (var fileStream = new FileStream(Path.Combine(filePath, fileName), FileMode.Create)) { await file.CopyToAsync(fileStream); } } return true; }
UploadFileDto:
public class UploadFileDto { public string FileHash { get; set; } public string FileName { get; set; } }
后台需要以下步驟:
1、對前端計算的文件哈希值進行查詢,返回數據庫中不存在的文件信息
2、將已存在的文件進行路徑指向,此時這些文件就不需要再次上傳,只要在數據庫中加一條路徑指向就可以了
3、對服務器不存在的文件進行逐一上傳
由於文件的hash算法是在前端實現,所有后台處理的方式有多種,大家可以根據自己的業務和需求進行調整。
前端(angular)的實現,前端的實現是采用開源的js包,所以任何框架均可實現
首先安裝js-sha256包:npm install js-sha256
在需要上傳的模塊中進行引用: import { sha256 } from 'js-sha256';
這里使用的是primeng中的上傳組件,在選擇文件后進行哈希計算:
onSelect(event, form): void { if (form.files.length == 1) { this.uploadDto.pop(); } for (const file of event.files) { let self = this; let fr = new FileReader(); var upload = new UploadFileDto(); upload.fileName = file.name; fr.readAsArrayBuffer(file); fr.onload = function (data: any) { let fi = data.target.result; var hash = sha256(fi); upload.fileHash = hash; self.uploadDto.push(upload); } } }
點擊上傳:
myUploader(event, form): void { if (event.files.length == 0) { return; } this.uploading = true; this._filesUploadService.check(this.uploadDto) .subscribe(result => { this.uploadDto = result; }); let input = new FormData(); for (const file of event.files) { var uploadFile = this.uploadDto.find(_ => _.fileName == file.name); if (uploadFile) { input.append('files', file); } } this._httpClient .post(this.uploadUrl, input) .subscribe(result => { if (result) { for (const file of event.files) { this.uploadedFiles.push(file); } form.clear(); this.uploading = false; this.message.success('上傳成功!'); } else { this.uploading = false; this.message.error('上傳失敗!'); } }, error=>{ this.message.error('上傳失敗!'); }); }
其他開發人員可能對angular語法有點難懂,其實核心只有三步:
1、選擇文件並對文件進行計算
2、上傳計算的文件信息並獲取返回信息
3、對返回的文件信息進行包裝,將文件流一並傳入接口
總結:以上只是對文件校驗上傳的簡單實現,如有不足之處,還請多多賜教。