File compression before upload on the client-side

2019-02-01 13:19发布

问题:

Basically I'll be working with large XML files (approx. 20 - 50 MB). These files needs to be uploaded on a server.

I know it isn't possible to touch the files with javascript, nor to implement HTTP compression on the client-side.

My question is that if any solution exists (flash / action script) that compresses a file and has a javascript API?

The scenario is this:

  1. Trying to upload 50 MB XML file
  2. Before upload a grab it with Javascript and send it to the compressor.
  3. Upload the compressed file instead of the original one.

回答1:

Flash's inbuilt implementation of ByteArray has a method (ByteArray::deflate to deflate the contents (of the bytearray) The deflate algorithm is the DEFLATE Compressed Data Format Specification version 1.3.

There;s also a ByteArray::compress method which compresses using the zlib algorithm

Hold on a bit, I'll write you some sample code to use this class and expose it to JavaScript.

EDIT

I've uploaded the file at http://www.filefactory.com/file/cf8a39c/n/demo5.zip

EDIT 2 For those who couldn't download the files:

My ActionScript code in demo5.fla (compiled to demo5.swf)

import flash.external.ExternalInterface;
import flash.net.FileReference;
import flash.events.Event;
import flash.utils.ByteArray;

if(ExternalInterface.available) {
    //flash.system.Security.allowDomain("localhost");
    ExternalInterface.addCallback("deflate", doDeflate);
    ExternalInterface.addCallback("compress", doCompress);
}

var method:String="deflate";
var b:ByteArray;
function doCompress(_data:String):void {
    method="compress";
    exec(_data);
}

function doDeflate(_data:String):void {
    method="deflate";
    exec(_data);
}

function exec(_data:String):void {
    b=new ByteArray();
    b.writeUTFBytes(_data);
    b.position=0;
    if(method=="compress") {
        b.compress();
    } else if(method=="deflate") {
        b.deflate();
    }
    executed();
}

function executed():void {
    if(ExternalInterface.available) {
        b.position=0;
        var str:String=b.readUTFBytes(b.bytesAvailable);
        ExternalInterface.call("onExec", str);
    }
}

My HTML code to embed the swf:

<button onclick="doDeflate()">Deflate</button>
<button onclick="doCompress()">Compress</button>
<div id="flashContent">
    <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="1" height="1" id="demo5" align="middle">
        <param name="movie" value="demo5.swf" />
        <param name="quality" value="high" />
        <param name="bgcolor" value="#ffffff" />
        <param name="play" value="true" />
        <param name="loop" value="true" />
        <param name="wmode" value="window" />
        <param name="scale" value="showall" />
        <param name="menu" value="true" />
        <param name="devicefont" value="false" />
        <param name="salign" value="" />
        <param name="allowScriptAccess" value="always" />

        <embed src="demo5.swf" quality="high" bgcolor="#869ca7"
             width="1" height="1" name="demo5" align="middle"
             play="true" loop="false" quality="high" allowScriptAccess="always"
             type="application/x-shockwave-flash"
             pluginspage="http://www.macromedia.com/go/getflashplayer">
        </embed>
    </object>
</div>

and finally the javascript code:

function doDeflate() {
    var data="fdg fhnkl,hgltrebdkjlgyu ia43uwriu67ri8m nirugklhvjsd fgvu";
    //DATA CONTAINS DATA TO BE DEFLATED
    thisMovie("demo5").deflate(data);
}

function doCompress() {
    var data="fdg fhnkl,hgltrebdkjlgyu ia43uwriu67ri8m nirugklhvjsd fgvu";
    //DATA CONTAINS DATA TO BE DEFLATED
    thisMovie("demo5").compress(data);
}

function onExec(data) {
    //DATA CONTAINS THE DEFLATED DATA
    alert(data);
}

function thisMovie(movieName) {
    if (navigator.appName.indexOf("Microsoft") != -1) {
        return window[movieName];
    } else {
        return document[movieName];
    }
}


回答2:

You can make use of JSZip. For input, it supports String/ArrayBuffer/Uint8Array/Buffer, but not blobs, which is what you get from an <input type="file"/> with javascript:

A File object is specific kind of a Blob, and can be used in any context that a Blob can

(link)

So you'll have to convert the blob/file to e.g. an ArrayBuffer first, e.g. using FileReader.readAsArrayBuffer(). Note that this function works asynchronously, demanding callback usage. There is also a FileReaderSync available, yet "This interface is only available in workers as it enables synchronous I/O that could potentially block", so I don't see any good in using it.

(EDIT. I'm not sure but I believe you can skip the blob->ArrayBuffer conversion now and simply zip the File object.)

This whole approach is specially useful if php's directive max_file_uploads was set to a small number by your webspace host, for now the only thing you'll have to worry about is upload_max_filesize

For reference, a code sample excerpt follows (using JQuery) for putting several files of one multiple file input in a zip before submitting:

// onclick:
var fileInput = $(':file');
var files = [];
$.each(fileInput[0].files, function(i, file) {
    files.push(file);
});

var zip = new JSZip();
function addFileToZip(n) {
    if(n >= files.length) {
        zippingComplete(zip.generate({type:"blob", compression:"deflate"}));
        return;
    }
    var file = files[n];                    
    var arrayBuffer;
    var fileReader = new FileReader();
    fileReader.onload = function() {
        arrayBuffer = this.result;
        zip.file(file.name, arrayBuffer);
        addFileToZip(n + 1);
    };
    fileReader.readAsArrayBuffer(file);
}
addFileToZip(0);

function zippingComplete(zip) {
    formData = new FormData();
    formData.append('fileZip', zip);
    formData.append("param1", "blah");
    $.ajax({
        data: formData,
        //... etc

Server-side-wise, you'll access $_FILES["fileZip"].



回答3:

If for whatever reason you can't get a solution to work in JavaScript for all major browsers, I know of an AS3 compression library here: http://code.google.com/p/ascompress/.

Also, a less cooler option, if your target users are somewhat tech savy why not have them upload a .zip file of the xml? Then on the server side you can unzip and process as needed.

Either way on the server side you'll want to unzip/decompress, which should be easy to google for solutions if you don't already have one in mind.



回答4:

With Silverlight, you can zip files on the client side, and this approach works in all major browsers. Moreover, you can interact with your Silverlight widget via JavaScript. Also, if a user needs to upload several files, your Silverlight widget can show a single dialog for selecting all the files. The only drawback is that your clients must install the Silverlight plugin.



回答5:

Consider reviewing this other stackoverflow post. Reading both answers paints a good picture of compression reality.

I'm considering implementing a Silverlight of Flex solution that compresses client side and if the user doesn't want to install it, compress and decompress the file server side. Will update this post when a solution is found.

Installing the control would be sold to the user as a time saver, which is normally true. For the server, it would be a bandwidth and compression processing saver.



回答6:

There some javascript library of huffman compression freely available, for example https://github.com/wilkerlucio/huffman_js but I think that your task is impossible because with javascript and html it's not possible to load huge data into the browser or client's memory.