在 javascript 中压缩一个 blob

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17130387/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 07:10:34  来源:igfitidea点击:

Compressing a blob in javascript

javascriptcompressionblob

提问by Fibericon

I need to send a blob to the server with ajax, but it can end up getting somewhat large, and I'd like to decrease upload time. I've tried jszip already, but that just gave me an empty file inside the zip. I've also tried btoa(), but it turns out that the encoded value just ends up being [object Blob] instead of the actual blob data. What are my options for compressing blobs?

我需要使用 ajax 向服务器发送一个 blob,但它最终会变得有点大,我想减少上传时间。我已经尝试过 jszip,但这只是在 zip 中给了我一个空文件。我也试过 btoa(),但结果是编码值最终是 [object Blob] 而不是实际的 blob 数据。我有哪些压缩 blob 的选项?

Here was the code I used for jszip:

这是我用于 jszip 的代码:

var zip = new JSZip();
zip.file("recording.wav", blobFile);
var content = zip.generate();

I then appended "content" to a FormData object and sent it to the server. On the server side, I decoded the POST data (from base64). The zip file opened just fine, but recording.wav was a 0 length file.

然后我将“内容”附加到 FormData 对象并将其发送到服务器。在服务器端,我解码了 POST 数据(来自 base64)。zip 文件打开得很好,但 Recording.wav 是一个长度为 0 的文件。

Additionally, I've tried using the LZW implementation found here. This was the additional code I used to compress it:

此外,我尝试使用此处找到的 LZW 实现。这是我用来压缩它的附加代码:

var compressed;
var reader = new FileReader();
reader.onload = function(event){
   compressed = LZW.compress(event.target.result);
};
reader.readAsText(blobFile);

However, decompressing it returns null.

但是,解压缩它返回空值。

回答by hoonto

Caveat: compressing things like audio files would be better done using an algorithm meant specifically for that type of data, perhaps something lossy. However, knowing how hard it was to find a reasonable lossless implementation as provided below, I'm very concerned that it will be hard to find a good implementation in Javascript for that type of data specifically that meets your needs.

警告:压缩音频文件之类的东西最好使用专门用于该类型数据的算法,可能是有损的。但是,知道找到如下提供的合理无损实现是多么困难,我非常担心很难在 Javascript 中为那种类型的数据找到一个很好的实现,特别是满足您的需求。

In any case, I've had this general need for compression/decompression in Javascript as well, and I needed the same algorithm to work both client (browser) and server-side (node.js) and I needed it to work on very large files. I had checked out jszip and I also tried that LZW algorithm among at least five or six others none of which satisfied the requirements. I can't remember what the issue was with each specifically, but suffice to say it is surprisingly hard to find a good and FAST compressor/decompressor in javascript that works both server and client side and handles large files! I tried at least a dozen different implementations of various compression algorithms, and finally settled with this one - it hasn't failed me yet!

无论如何,我对 Javascript 中的压缩/解压缩也有这种普遍的需求,我需要相同的算法来工作客户端(浏览器)和服务器端(node.js),我需要它在非常大文件。我已经检查了 jszip 并且我还尝试了 LZW 算法,其中至少有五六个其他算法都不满足要求。我不记得每个具体的问题是什么,但可以说很难在 javascript 中找到一个好的和快速的压缩器/解压缩器,它可以在服务器端和客户端工作并处理大文件!我尝试了各种压缩算法的至少十几种不同的实现,最后解决了这个问题——它还没有让我失望!



UPDATE

更新

This is the original source: https://code.google.com/p/jslzjb/source/browse/trunk/Iuppiter.js?r=2

这是原始来源:https: //code.google.com/p/jslzjb/source/browse/trunk/Iuppiter.js?r=2

By someone named Bear - thanks Bear, whoever you are, you're the best. It is LZJB: http://en.wikipedia.org/wiki/LZJB

一个叫熊的人——谢谢熊,不管你是谁,你都是最棒的。它是 LZJB:http: //en.wikipedia.org/wiki/LZJB



UPDATE 2

更新 2

  1. Corrected a problem with missing semicolon - should not give the object not a function error any longer.
  2. This implementation stops working on data less than about 80 characters in length. So I updated the example to reflect that.
  3. Realized the base64 encode/decode methods are in fact exposed on the object passed in for this version, so...
  4. Currently seeing what we can do about specific blob types - what for example the best approach would be for a image versus audio etc as that would be useful for JS folks in general... will update here with what is found.
  1. 更正了缺少分号的问题 - 不应再给对象非函数错误。
  2. 此实现停止处理长度小于 80 个字符的数据。所以我更新了这个例子来反映这一点。
  3. 意识到 base64 编码/解码方法实际上暴露在为此版本传入的对象上,所以......
  4. 目前正在了解我们可以对特定的 blob 类型做些什么——例如,最好的方法是图像与音频等,因为这对 JS 人员通常很有用......将在此处更新发现的内容。


UPDATE 3

更新 3

There is a much better wrapper around the original Iuppiter source from Bear than the one I posted below. It is written by cscott and on github here: https://github.com/cscott/lzjb

与我在下面发布的相比,Bear 的原始 Iuppiter 源有一个更好的包装器。它是由 cscott 和在 github 上编写的:https: //github.com/cscott/lzjb

I'll be switching to this one, as it does streams as well.

我将切换到这个,因为它也可以流式传输。

Below is an example in Node.js of its use with a wav file. But before copying the example, let me give you the terrible news first, at least for this one wav file that I tried:

下面是 Node.js 中使用 wav 文件的示例。但是在复制示例之前,让我先告诉您一个可怕的消息,至少对于我尝试过的这个 wav 文件:

63128 Jun 19 14:09 beep-1.wav 
63128 Jun 19 17:47 beep-2.wav
89997 Jun 19 17:47 beep-2.wav.compressed 

So it successfully regenerated the wav (and it played). However, the compressed one appears to be larger than the original. Well shoot. In any case, might be good to try on your data, you never know, you might get lucky. Here's the code I used:

所以它成功地重新生成了 wav(并播放了)。但是,压缩后的似乎比原始的要大。好拍。无论如何,尝试您的数据可能会很好,您永远不会知道,您可能会很幸运。这是我使用的代码:

var fs = require('fs');
var lzjb = require('lzjb');

fs.readFile('beep-1.wav', function(err, wav){

    // base 64 first
    var encoded = wav.toString('base64');
    // then utf8 - you  don't want to go utf-8 directly
    var data = new Buffer(encoded, 'utf8');
    // now compress
    var compressed = lzjb.compressFile(data, null, 9);
    // the next two lines are unnecessary, but to see what kind of
    // size is written to disk  to compare with the original binary file
    var compressedBuffer = new Buffer(compressed, 'binary');
    fs.writeFile('beep-2.wav.compressed', compressedBuffer, 'binary', function(err) {});
    // decompress
    var uncompressed = lzjb.decompressFile(compressed);
    // decode from utf8 back to base64
    var encoded2 = new Buffer(uncompressed).toString('utf8');
    // decode back to binary original from base64
    var decoded = new Buffer(encoded2, 'base64');
    // write it out, make sure it is identical
    fs.writeFile('beep-2.wav', decoded, function(err) {});

});

At the end of the day, I think its going to be too difficult to achieve any level of compression on most forms of binary data that isn't clobbered by the resulting base64 encoding. The days of control characters for terminals still haunt us to this day. You could try upping to a different base, but that has its risks and issues as well.

归根结底,我认为在大多数形式的二进制数据上实现任何级别的压缩都太困难了,而不会受到由此产生的 base64 编码的破坏。直到今天,终端控制字符的日子仍然困扰着我们。您可以尝试升级到不同的基础,但这也有其风险和问题。

See this for example: What is the most efficient binary to text encoding?

例如,请参见: 什么是最有效的二进制到文本编码?

And this: Why don't people use base128?

还有这个: 为什么人们不使用 base128?



One thing though, definitely before you accept the answer, please please try it out on your blob, I've mainly used it for compressing utf-8, and I'd like to be sure it works on your specific data.

不过有一件事,在您接受答案之前,请务必在您的 blob 上尝试一下,我主要用它来压缩 utf-8,我想确保它适用于您的特定数据。

In any case, here it is!

无论如何,它来了!

/**
$Id: Iuppiter.js 3026 2010-06-23 10:03:13Z Bear $

Copyright (c) 2010 Nuwa Information Co., Ltd, and individual contributors.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.

  3. Neither the name of Nuwa Information nor the names of its contributors
     may be used to endorse or promote products derived from this software
     without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

$Author: Bear $
$Date: 2010-06-23 18:03:13 +0800 (星期三, 23 六月 2010) $
$Revision: 3026 $
*/
var fastcompressor = {};
(function (k) {
    k.toByteArray = function (c) {
        var h = [],
            b, a;
        for (b = 0; b < c.length; b++) a = c.charCodeAt(b), 127 >= a ? h.push(a) : (2047 >= a ? h.push(a >> 6 | 192) : (65535 >= a ? h.push(a >> 12 | 224) : (h.push(a >> 18 | 240), h.push(a >> 12 & 63 | 128)), h.push(a >> 6 & 63 | 128)), h.push(a & 63 | 128));
        return h
    };
    k.Base64 = {
        CA: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
        CAS: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
        IA: Array(256),
        IAS: Array(256),
        init: function () {
            var c;
            for (c = 0; 256 > c; c++) k.Base64.IA[c] = -1, k.Base64.IAS[c] = -1;
            c = 0;
            for (iS = k.Base64.CA.length; c < iS; c++) k.Base64.IA[k.Base64.CA.charCodeAt(c)] = c, k.Base64.IAS[k.Base64.CAS.charCodeAt(c)] = c;
            k.Base64.IA["="] = k.Base64.IAS["="] = 0
        },
        encode: function (c, h) {
            var b, a, d, e, m, g, f, l, j;
            b = h ? k.Base64.CAS : k.Base64.CA;
            d = c.constructor == Array ? c : k.toByteArray(c);
            e = d.length;
            m = 3 * (e / 3);
            g = (e - 1) / 3 + 1 << 2;
            a = Array(g);
            for (l = f = 0; f < m;) j = (d[f++] & 255) << 16 | (d[f++] & 255) << 8 | d[f++] & 255, a[l++] = b.charAt(j >> 18 & 63), a[l++] = b.charAt(j >> 12 & 63), a[l++] = b.charAt(j >> 6 & 63), a[l++] = b.charAt(j & 63);
            f = e - m;
            0 < f && (j = (d[m] &
                255) << 10 | (2 == f ? (d[e - 1] & 255) << 2 : 0), a[g - 4] = b.charAt(j >> 12), a[g - 3] = b.charAt(j >> 6 & 63), a[g - 2] = 2 == f ? b.charAt(j & 63) : "=", a[g - 1] = "=");
            return a.join("")
        },
        decode: function (c, h) {
            var b, a, d, e, m, g, f, l, j, p, q, n;
            b = h ? k.Base64.IAS : k.Base64.IA;
            c.constructor == Array ? (d = c, m = !0) : (d = k.toByteArray(c), m = !1);
            e = d.length;
            g = 0;
            for (f = e - 1; g < f && 0 > b[d[g]];) g++;
            for (; 0 < f && 0 > b[d[f]];) f--;
            l = "=" == d[f] ? "=" == d[f - 1] ? 2 : 1 : 0;
            a = f - g + 1;
            j = 76 < e ? ("\r" == d[76] ? a / 78 : 0) << 1 : 0;
            e = (6 * (a - j) >> 3) - l;
            a = Array(e);
            q = p = 0;
            for (eLen = 3 * (e / 3); p < eLen;) n = b[d[g++]] << 18 | b[d[g++]] <<
                12 | b[d[g++]] << 6 | b[d[g++]], a[p++] = n >> 16 & 255, a[p++] = n >> 8 & 255, a[p++] = n & 255, 0 < j && 19 == ++q && (g += 2, q = 0);
            if (p < e) {
                for (j = n = 0; g <= f - l; j++) n |= b[d[g++]] << 18 - 6 * j;
                for (b = 16; p < e; b -= 8) a[p++] = n >> b & 255
            }
            if (m) return a;
            for (n = 0; n < a.length; n++) a[n] = String.fromCharCode(a[n]);
            return a.join("")
        }
    };
    k.Base64.init();
    NBBY = 8;
    MATCH_BITS = 6;
    MATCH_MIN = 3;
    MATCH_MAX = (1 << MATCH_BITS) + (MATCH_MIN - 1);
    OFFSET_MASK = (1 << 16 - MATCH_BITS) - 1;
    LEMPEL_SIZE = 256;
    k.compress = function (c) {
        var h = [],
            b, a = 0,
            d = 0,
            e, m, g = 1 << NBBY - 1,
            f, l, j = Array(LEMPEL_SIZE);
        for (b = 0; b < LEMPEL_SIZE; b++) j[b] =
            3435973836;
        c = c.constructor == Array ? c : k.toByteArray(c);
        for (b = c.length; a < b;) {
            if ((g <<= 1) == 1 << NBBY) {
                if (d >= b - 1 - 2 * NBBY) {
                    f = b;
                    for (d = a = 0; f; f--) h[d++] = c[a++];
                    break
                }
                g = 1;
                m = d;
                h[d++] = 0
            }
            if (a > b - MATCH_MAX) h[d++] = c[a++];
            else if (e = (c[a] + 13 ^ c[a + 1] - 13 ^ c[a + 2]) & LEMPEL_SIZE - 1, l = a - j[e] & OFFSET_MASK, j[e] = a, e = a - l, 0 <= e && e != a && c[a] == c[e] && c[a + 1] == c[e + 1] && c[a + 2] == c[e + 2]) {
                h[m] |= g;
                for (f = MATCH_MIN; f < MATCH_MAX && c[a + f] == c[e + f]; f++);
                h[d++] = f - MATCH_MIN << NBBY - MATCH_BITS | l >> NBBY;
                h[d++] = l;
                a += f
            } else h[d++] = c[a++]
        }
        return h
    };
    k.decompress = function (c,
        h) {
        var b, a = [],
            d, e = 0,
            m = 0,
            g, f, l = 1 << NBBY - 1,
            j;
        b = c.constructor == Array ? c : k.toByteArray(c);
        for (d = b.length; e < d;) {
            if ((l <<= 1) == 1 << NBBY) l = 1, f = b[e++];
            if (f & l)
                if (j = (b[e] >> NBBY - MATCH_BITS) + MATCH_MIN, g = (b[e] << NBBY | b[e + 1]) & OFFSET_MASK, e += 2, 0 <= (g = m - g))
                    for (; 0 <= --j;) a[m++] = a[g++];
                else break;
                else a[m++] = b[e++]
        }
        if (!("undefined" == typeof h ? 0 : h)) {
            for (b = 0; b < m; b++) a[b] = String.fromCharCode(a[b]);
            a = a.join("")
        }
        return a
    }
})(fastcompressor);

And if memory serves... here's how you use it:

如果没有记错的话……这就是你如何使用它:

var compressed = fastcompressor.compress("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); // data less than this length poses issues.
var decompressed = fastcompressor.decompress(compressed);

Rgds....Hoonto/Matt

Rgds....Hoonto/Matt

Also, what I've posted is minified but beautified, and very slightly adapted for ease-of-use. Check the link in the update above for the original stuff.

此外,我发布的内容是缩小但美化的,并且为了易于使用而稍微进行了调整。检查上面更新中的链接以获取原始内容。

回答by MarmiK

JS Zip will work fine just correct your syntax..

JS Zip 可以正常工作,只需纠正您的语法..

function create_zip() {
    var zip = new JSZip();
    zip.add("recording.wav", blobfile);//here you have to give blobFile in the form of raw bits >> convert it in json notation.. or stream .. 
    zip.add("hello2.txt", "Hello Second World\n");//this is optional..
    content = zip.generate();
    location.href="data:application/zip;base64," + content;
}

you can add multiple files too..

您也可以添加多个文件..

Just zip.file will become zip.add

只是 zip.file 会变成 zip.add

and then zip.generate() will do the rest.. as you have done,

然后 zip.generate() 将完成剩下的工作......就像你所做的那样,

or refer old Postits last part of JavaScript, and NativeBridge will be helpful if you can utilize, in this post user records using Objective C that you can ignore, but sends this object using JavaScript and Socket that you can/may utilize..

或参考旧帖子其 JavaScript 的最后一部分,如果您可以使用 NativeBridge 会有所帮助,在此帖子中,用户使用 Objective C 记录您可以忽略的内容,但使用您可以/可能使用的 JavaScript 和 Socket 发送此对象..

I hope this will do ... :)

我希望这会做... :)