功能點:前端
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),配置一下參數就好了。
部分代碼參考博友,找不到出處了,請涼解。