Asp.Net Core 2.0 WebUploader FastDfs 文件上傳 分段上傳

功能點:前端

1. 使用.net core 2.0 實現文件上傳ios

2. 使用webuploader實現單文件,多文件上傳web

3. 使用webuploader實現大文件的分段上傳。數據庫

4. 使用webuploader與FastDfs實現文件上傳。緩存

5. 經過依賴注入能夠很方便地實現本地文件存儲切換爲FastDfs服務器

 

主要功能:併發

直接上代碼:分佈式

1. 使用.net core 2.0 實現文件上傳ide

        public ActionResult Upload(IFormCollection files)
        {
            var result = new UploadResult();

            foreach (var item in files.Files)
            {
                #region 把文件流轉化爲字節流
                byte[] buffer = new byte[item.Length];

                Stream fs = item.OpenReadStream();

                fs.Read(buffer, 0, buffer.Length);
                #endregion

                UploadConfig config = new UploadConfig
                {
                    Buffer = buffer,
                    FileName = item.FileName,
                    Chunked = files.Keys.Contains("chunk"),
                    PreviosName = files["previosName"]
                };

                result = _provider.Upload(config);
            }
            return Json(result);
        }

這裏要注意參數,使用的是IFormCollection,沒有使用IFormFile,使用後者的話,不能接收前臺發送的自定義參數。雖然我用了fforeach循環,但實現上它的files count只有一個,無論前臺發送的是一個文件,仍是多個文件,這點還要研究一下,是否是有配置。ui

        private readonly IFileSystemProvider _provider;

        public HomeController(IFileSystemProvider provider)
        {
            _provider = provider;

        }
IFileSystemProvider 爲定義的接口,分別有本地文件存儲,和分佈式文件存儲兩種實現方式,後面會奉上代碼。

2. 使用webuploader實現單文件,多文件上傳
這裏主要上前臺代碼:
        // 實例化
        uploader = WebUploader.create({
            pick: {
                id: '#filePicker',
                label: '點擊選擇文件'
            },
            dnd: '#dndArea',
            paste: '#uploader',
            swf:'lib/webuploader/Uploader.swf',
            chunked: true, //分片處理大文件
            chunkSize: 1 * 1024 * 1024,
            server: home/upload,
            disableGlobalDnd: true,
            threads: 1, //上傳併發數
            fileNumLimit: 300,
            compress: false, //圖片在上傳前不進行壓縮
            fileSizeLimit: 1024 * 1024 * 1024,    // 1024 M
            fileSingleSizeLimit: 1024 * 1024 * 1024    // 1024 M
        });

初始化webuploader這裏要注意threads我想的是1,若是分段上傳,幾個分段同時發送的話,後臺若是寫入是個問題,特別是使用fastdfs, .net版本的sdk還不支持指定位置寫入的功能。

注意到一個事件:它是實現分段上傳重中之重

        uploader.on('uploadAccept', function (file, response) {
            if (file.chunk + 1 !== file.chunks) {
                $.extend(uploader.options.formData, { previosName: response.filePath });
            } else {
                $.extend(uploader.options.formData, { previosName: "" });
            }
            return true;
        });

這裏着重解釋一下。看到網上關於文件分段上傳,都是在服務器端用臨時文件記錄着文件上傳的地址,當有分段信息到達時,會查詢臨時文件,取出文件地址,而後再往這個地址追加文件流。

這樣增長了服務器端的複雜度,並且若是是分佈式部署的話,會存在找到臨時文件的問題,進而會考慮到數據庫和分佈式緩存的解決方案,更加複雜了。

這裏簡單說明一下解決思路:

1) 後臺:第一段上傳成功後,返回文件地址。如代碼的 return Json(result);

2) 前臺:使用uploadAccept事件接收文件信息和後臺返回地址。

3)前臺:在uploadAccept事件中:添加日後臺發送的參數previosName:第一段返回的文件地址。

   這裏要注意若是是多個文件上傳的話,第二個文件的第一個分段不能帶有第一個文件的上傳地址,如上代碼所示

4) 後臺:如上面的代碼所示,接收前臺發送的文件地址,作文件的追加,便可。

 

3. 使用webuploader實現大文件的分段上傳。

下面是服務端的代碼:

        public UploadResult Upload(UploadConfig config)
        {
            //相對路徑
            string path = GetFileName(config);

            //根據路徑建立一個新的文件,並在指定位置寫入字節,若是文件已存在,就追加
            using (FileStream fs = File.OpenWrite(Path.Combine(_option.RootFilePath, path)))
            {
                if (fs.CanWrite)
                {
                    fs.Seek(fs.Length, SeekOrigin.Current);
                    fs.Write(config.Buffer, 0, config.Buffer.Length);
                    fs.Flush();
                    fs.Close();
                }
            }

            return new UploadResult { FilePath = path, OriginalName = config.FileName };
        }

        private string GetFileName(UploadConfig config)
        {
            string path;

            if (string.IsNullOrEmpty(config.PreviosName))
            {
                path = Path.Combine(subDirectory, $"{GuidTo16String()}.{config.FileName}"); //返回的相對路徑和文件名

                CheckSavePath();
            }
            else
            {
                path = config.PreviosName;
                if (string.IsNullOrEmpty(config.PreviosName))
                {
                    throw new ArgumentNullException("PreviosName 參數不能爲空");
                }
            }

            return path;
        }

        private string CheckSavePath()
        {
            var path = Path.Combine(_option.RootFilePath, subDirectory);

            // 如何路徑不存在,就建立文件路徑
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            return path;
        }

這裏關注一下GetFileName方法:代碼很簡單,就不作說明了。

4. 使用webuploader與FastDfs實現文件上傳

前端代碼和服務端的action的代碼是同樣的。主要是寫文件的方式不同,主要代碼以下:

        public UploadResult Upload(UploadConfig config)
        {
            if (string.IsNullOrEmpty(config.PreviosName))
            {
                var fileName = string.Empty;
                var storageNode = _client.GetStorageNode(config.GroupName);

                if (!config.Chunked)
                {
                    fileName = _client.UploadFile(storageNode, config.Buffer, Path.GetExtension(config.FileName));
                }
                else
                {
                    //分段上傳須要調用這個方法
                    fileName = _client.UploadAppenderFile(storageNode, config.Buffer, Path.GetExtension(config.FileName));
                }

                return new UploadResult { FilePath = fileName, OriginalName = config.FileName };
            }
            else
            {
                //分段上傳:須要提供上傳GroupName, 文件上傳地址PreviosName,文件上傳內容filebody
                //續傳 地址config.PreviosName
                _client.AppendFile(config.GroupName, config.PreviosName, config.Buffer);

                return new UploadResult { FilePath = config.PreviosName, OriginalName = config.FileName };
            }
        }

這裏注意分段上傳的文件,第一段上傳使用的方法是UploadAppenderFile,後續使用的是AppendFile。

鏈接FastDfs的代碼以下:

    public class FastDfsClientProvider : IFastDfsClientProvider
    {
        private readonly FastDfsOption _options;

        public FastDfsClientProvider(FastDfsOption options)
        {
            _options = options;
        }

        public FastDfsClient GetClient()
        {
            return new FastDfsClient(new List<IPEndPoint>
            {
                new IPEndPoint(IPAddress.Parse(_options.IpAddress), _options.Port)
            });
        }
    }

 

5. 經過依賴注入能夠很方便地實現本地文件存儲切換爲FastDfs

代碼以下:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            
            services.AddTransient<IFileSystemProvider>(m=> {
                IHostingEnvironment env = m.GetService<IHostingEnvironment>();

                LocalFsOption lOption = new LocalFsOption
                {
                    RootFilePath = Path.Combine(env.ContentRootPath, "Files")
                };
                return new LocalFsProvider(lOption);
            });
        }

若是想換實現的話 改一個return new LocalFsProvider(lOption),配置一下參數就好了。

 

部分代碼參考博友,找不到出處了,請涼解。

相關文章
相關標籤/搜索