前一段時間作附件的上傳和下載功能。期間遇到很多問題,網上找的方法都不算完整。這裏把前端和後端的實現都整理一下。前端
須要的東西:JQuery,Pako.js,base64.js。其中pako/base64是爲了對上傳的文件進行壓縮而使用。若是前端有對文件進行壓縮,那麼後端也應該對應進行解壓。node
上傳最簡單的實現:前端選擇一個文件-讀取文件信息-經過ajax請求後端方法把文件信息傳輸並保存(固然,當文件太大,在客戶端應該要分批讀取並上傳,在服務端也是依次分批保存)git
<input type="file" id="filePicker" />
$(document).ready(function () { $(document).on("change", "#filePicker", onFileUpload); }); function onFileUpload(e) { if (!window.FileReader) { // 使用了HTML 5 的 FileReader API。目前大部分瀏覽器都能支持。 alert("該瀏覽器不支持HTML5,請升級或者更換其它瀏覽器。"); return; } var fileInfo = e.currentTarget.files[0]; var fileName = fileInfo.name; var fileSize = fileInfo.size; var fileReader = new FileReader(); //var blob = fileInfo.slice(0, fileSize); //fileReader.readAsArrayBuffer(blob); fileReader.readAsArrayBuffer(fileInfo); fileReader.onload = function (result) { var pakoString = getUploadingFileContentString(this.result); $.ajax({ url: "http://localhost/Server/ashx/FileProcess.ashx", //用通常處理程序接收請求 type: "POST", data: { fileContent: pakoString, fileName: fileName }, success: function (data) { if (data == "True") { alert("上傳成功!"); } else { alert("上傳失敗"); } }, error: function (e) { alert(e.responseText); } }); } } function getUploadingFileContentString(readResult) { if (readResult == null) { return null; } var fileContentArr = new Uint8Array(readResult); var fileContentStr = ""; for (var i = 0; i < fileContentArr.length; i++) { fileContentStr += String.fromCharCode(fileContentArr[i]); } //若是不壓縮,直接轉base64 string進行傳輸 //window.btoa: 將ascii字符串或二進制數據轉換成一個base64編碼過的字符串,該方法不能直接做用於Unicode字符串. //var base64FileString = window.btoa(fileContentStr); //return base64FileString; //壓縮 var pakoBytes = pako.gzip(fileContentStr); var pakoString = fromByteArray(pakoBytes); return pakoString; }
public class FileProcess : IHttpHandler { public void ProcessRequest(HttpContext context) { UploadFile(context); } private void UploadFile(HttpContext context) { var fileName = context.Request["fileName"]; var compressedFileContent = context.Request["fileContent"]; //若是前端沒有對文件進行壓縮,那麼直接獲取文件的字節數組便可 //byte[] fileBytes = Convert.FromBase64String(compressedFileContent); //解壓 var fileContent = GZip.GZipDecompress(compressedFileContent); byte[] fileBytes = Utils.ConvertJSBytes2Str(fileContent); bool isSavedSuccess = Utils.SaveFile2Disk(fileBytes, fileName, isUploadPartly); context.Response.Write(isSavedSuccess); } }
//其中GZipDecompress/ConvertJSBytes2Str/SaveFile2Disk實現以下 public static string GZipDecompress(string zipString) { if (string.IsNullOrEmpty(zipString)) { return string.Empty; } byte[] zipBytes = Convert.FromBase64String(zipString); return System.Text.Encoding.UTF8.GetString(Decompress(zipBytes)); } private static byte[] Decompress(byte[] zipData) { MemoryStream m = new MemoryStream(zipData); GZipStream zipStream = new GZipStream(m, CompressionMode.Decompress); MemoryStream outStream = new MemoryStream(); byte[] buffer = new byte[1024]; while (true) { var readCount = zipStream.Read(buffer, 0, buffer.Length); if (readCount <= 0) { break; } outStream.Write(buffer, 0, readCount); } zipStream.Close(); return outStream.ToArray(); } public static byte[] ConvertJSBytes2Str(string fileContent) { if (string.IsNullOrEmpty(fileContent)) { return null; } // JS中,String.fromCharCode接受Unicode字符,並轉成字符串 byte[] fileBytes = System.Text.Encoding.Unicode.GetBytes(fileContent); byte[] adjustedFileBytes = new byte[fileBytes.Length / 2]; var index = 0; for (var i = 0; i < fileBytes.Length; i = i + 2) { adjustedFileBytes[index] = fileBytes[i]; index++; } return adjustedFileBytes; } public static bool SaveFile2Disk(byte[] fileContent, string fileName, bool isSavedPartly = false) { if (fileContent == null || fileContent.Length == 0) { throw new ArgumentNullException("文件內容不能爲空!"); } if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException("文件名不能爲空!"); } var targetFloder = HttpContext.Current.Server.MapPath("~/UploadFileFloder/"); var fullPath = Path.Combine(targetFloder, fileName); DirectoryInfo di = new DirectoryInfo(targetFloder); if (di.Exists == false) { di.Create(); } FileStream fileStream; try { if (isSavedPartly) { fileStream = new FileStream(fullPath, FileMode.Append, FileAccess.Write, FileShare.Read, 1024); } else { fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.ReadWrite); } } catch (Exception ex) { //throw ex; //write log return false; } try { fileStream.Write(fileContent, 0, fileContent.Length); } catch (IOException ex) { //write log return false; } finally { fileStream.Flush(); fileStream.Close(); fileStream.Dispose(); } return true; }
這樣,一個簡單的完整的前端到後端的文件上傳保存就完成了github
下載文件。(這裏簡化程序,服務端指定的文件夾裏面已經有待下載的file1.txt)ajax
<input type="button" id="downloadFileBtn" value="下載"/>
$(document).on("click", "#downloadFileBtn", downloadFile); function downloadFile() { var fileName = "file1.txt"; $.ajax({ url: "http://localhost/Server/ashx/FileProcess.ashx", type: "POST", success: function (data) { var fileContent = window.atob(data); saveFile(fileName, fileContent); }, error: function (e) { alert(e.responseText); } }); }
function saveFile(fileName, fileContent) { var byteArr = new Array(fileContent.length); for (var i = 0; i < fileContent.length; i++) { byteArr[i] = fileContent.charCodeAt(i); } var blob = new Blob([new Uint8Array(byteArr)], { type: "application/octet-stream" }); var url = window.URL.createObjectURL(blob); var a = document.createElement("a"); if ("download" in a) { // 支持download屬性 document.body.appendChild(a); a.style = "display:none";
a.href = url; //download屬性IE不支持。。。 a.download = fileName; a.click(); // 觸發下載 //revokeObjectURL會致使firefox不能下載。。。 //window.URL.revokeObjectURL(url); document.body.removeChild(a); } else { //IE 10+ if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { return navigator.msSaveOrOpenBlob(blob, name); } } }
//ProcessRequest中調用Download
public void Download(HttpContext context)
{ var filePath = Path.Combine(HttpContext.Current.Server.MapPath("~/UploadFileFloder/"), "file1.txt"); byte[] fileContentBytes = Utils.GetByteArrayByFilePath(filePath); context.Response.Write(Convert.ToBase64String(fileContentBytes)); } //其中,GetByteArrayByFilePath實現以下 public static byte[] GetByteArrayByFilePath(string fileFullPath) { if (string.IsNullOrEmpty(fileFullPath)) { return null; } FileStream fs = null; byte[] fileContent = null; try { FileInfo fi = new FileInfo(fileFullPath); fileContent = new byte[fi.Length]; //fs = new FileStream(fileFullPath, FileMode.Open); fs = File.OpenRead(fileFullPath); fs.Read(fileContent, 0, fileContent.Length); } catch (Exception ex) { return null; } finally { if (fs != null) { fs.Close(); } } return fileContent; }
這樣,一個簡單的完整的文件下載功能就實現了npm