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.jsbase64.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 压缩字符串和二进制的主要内容,如果未能解决你的问题,请参考以下文章

Swoole WebSoctet 使用 zlib 压缩之 PHP 与 pako.js

JS GZIP压缩

JS.压缩&解压缩

JavaScript文件上传和下载

gulp压缩整站方法(html/css/js/image)

ts里已经把sourcemap写成true了为啥还是不能转换成js