文件hash、上傳,實現文件上傳重復驗證


在平台開發中,我們往往對性能要求十分嚴苛,每一個字段、接口都有嚴格的要求。

系統中文件流操作十分占用資源,這里為大家介紹對文件上傳進行哈希校驗---同一文件只允許上傳一次到服務器,其他的上傳只要指向文件地址即可。

首先需要設計一張用於文件管理的業務表:

    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、對返回的文件信息進行包裝,將文件流一並傳入接口

 

總結:以上只是對文件校驗上傳的簡單實現,如有不足之處,還請多多賜教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM