Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File compression before upload on the client-side

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.
like image 432
feketegy Avatar asked Dec 04 '11 17:12

feketegy


People also ask

Is it always recommended to compress data before transfer?

In the context of network data transport, "Should I compress?" is a common question. But the answer can get complicated, depending on several factors. The most important thing to remember is that compression can actually make your data move much slower, so it should not be used without some consideration.

Does compressing a file make it faster to upload?

For most well-compressable files, you're already there. Compressing and uploading smaller file will be faster than uploading uncompressed, bigger data.

What are the 2 main reasons to do file compression?

The main advantages of compression are reductions in storage hardware, data transmission time, and communication bandwidth. This can result in significant cost savings. Compressed files require significantly less storage capacity than uncompressed files, meaning a significant decrease in expenses for storage.


2 Answers

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"].

like image 170
phil294 Avatar answered Sep 28 '22 10:09

phil294


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];
    }
}
like image 42
Pranav Hosangadi Avatar answered Sep 28 '22 10:09

Pranav Hosangadi