TS/JS 使用pako.js 压缩字符串和二进制
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TS/JS 使用pako.js 压缩字符串和二进制相关的知识,希望对你有一定的参考价值。
参考技术A 因为项目需要压缩字符串和二进制,找到了pako这个库:https://github.com/nodeca/pako
https://gitee.com/renew_old_romance/pako/tree/master
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/pako/index.d.ts
参考 javascript 简单实现Gzip 压缩字符串 基于pako.js
因为字符串需要与后端通讯,所以使用了bota/atob进行base64编码。
关于字符串与二进制处理,可以参考 jsmpeg系列一 基础知识 字符处理 ArrayBuffer TypedArray ,其中提到了ArrayBuffer与字符串的互相转换。
ArrayBuffer转为字符串,或者字符串转为ArrayBuffer,有一个前提,即字符串的编码方法是确定的。假定字符串采用UTF-16编码(JavaScript的内部编码方式),可以自己编写转换函数。
但是,ab2str这种写法,在实际使用中,如果buf过大,会有 Maximum call stack size exceeded 堆栈溢出。
可以参考 javascript - js数组转字符串 - 在字符串和ArrayBuffers之间转换 ,改为for的写法:
JavaScript文件上传和下载
前一段时间做附件的上传和下载功能。期间遇到不少问题,网上找的方法都不算完整。这里把前端和后端的实现都整理一下。
需要的东西:JQuery,Pako.js,base64.js。其中pako/base64是为了对上传的文件进行压缩而使用。如果前端有对文件进行压缩,那么后端也应该对应进行解压。
上传最简单的实现:前端选择一个文件-读取文件信息-通过ajax请求后端方法把文件信息传输并保存(当然,当文件太大,在客户端应该要分批读取并上传,在服务端也是依次分批保存)
- HTML只需要一个input
<input type="file" id="filePicker" />
- JS实现
$(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; }这样,一个简单的完整的前端到后端的文件上传保存就完成了
下载文件。(这里简化程序,服务端指定的文件夹里面已经有待下载的file1.txt)
- HTML
<input type="button" id="downloadFileBtn" value="下载"/>
- JS
$(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; }
这样,一个简单的完整的文件下载功能就实现了
以上是关于TS/JS 使用pako.js 压缩字符串和二进制的主要内容,如果未能解决你的问题,请参考以下文章