客户端上传前的文件压缩

16

我的工作将主要涉及处理大型XML文件(约20-50 MB)。这些文件需要上传到服务器。

我知道不能使用JavaScript操作文件,也无法在客户端实现HTTP压缩。

我的问题是:是否存在任何解决方案(例如Flash / ActionScript),可以对文件进行压缩并具有JavaScript API?

具体情况如下:

  1. 尝试上传50 MB的XML文件
  2. 在上传之前使用JavaScript获取文件并将其发送到压缩器。
  3. 上传压缩后的文件而不是原始文件。

我找到了这个,但我从未使用过它(这里也没有Flash):http://jszip.stuartk.co.uk/ - AsTheWormTurns
谢谢提供链接,但我需要一个能在所有主流浏览器中运行的解决方案,如IE7+、FF、Safari和Chrome。 - feketegy
6个回答

7
您可以使用JSZip。对于输入,它支持String/ArrayBuffer/Uint8Array/Buffer,但不支持从javascript中获取的blob,这是您从<input type="file"/>中获取的内容:

File对象是Blob的一种特定类型,可以在任何Blob可用的上下文中使用

(链接) 所以您需要先将blob/file转换为ArrayBuffer,例如使用FileReader.readAsArrayBuffer()。请注意,此函数是异步工作的,需要使用回调。也有一个FileReaderSync可用,但“此接口仅在workers中可用,因为它启用了可能会阻塞的同步I/O”,因此我认为没有使用它的好处。
(编辑:我不确定,但我相信现在可以跳过blob->ArrayBuffer转换,直接压缩File对象。)
如果您的webspace主机将php的指令max_file_uploads设置为一个较小的数字,则整个方法特别有用,因为现在您唯一需要担心的是upload_max_filesize 以下是一个代码示例摘录(使用JQuery),用于在提交之前将一个multiple文件输入的多个文件压缩到一个zip中:
// 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

在服务器端,你可以访问$_FILES["fileZip"]


1
你可以在版本3+中跳过blob->ArrayBuffer转换。但对于旧版本(例如我的2.6版本),你的解决方案非常有帮助。 - U.P
感谢@phil294,这真的很有帮助,但我能减小或压缩结果文件大小吗?我有多个PDF文件,想把它们全部放在一个ZIP文件中,所以我能从客户端减小总文件大小吗? - Khalid
1
@Immortal 选项 compression: 'deflate' 已经让 JSZip 压缩了 zip 结果。你可以通过增加压缩选项到等级 9 来进一步提高压缩比率,参见:https://stuk.github.io/jszip/documentation/api_jszip/generate_async.html - phil294
@phil294 大兄弟,非常感谢,它有效了,我将文件大小减小了一半,我能再进一步缩小吗? - Khalid
1
@Immortal 不是用JSZip,不行...你可以尝试其他能够启用压缩算法的包,比如.tar.xz,它可以将所有PDF文件合并压缩,而不是单独压缩,可能会有更高的压缩比。但我没有使用过任何这样的库。此外,根据您生成PDF的方式,您可能可以在不压缩的情况下更改其大小。例如,如果您使用像html2canvas这样的工具生成PDF,则它们会很大,因为PDF基本上是图像,而不是仅文本的PDF。 - phil294

6
Flash的内置ByteArray实现了一个方法(ByteArray::deflate)来压缩字节数组的内容。压缩算法是DEFLATE Compressed Data Format Specification version 1.3
还有一个ByteArray::compress方法,使用zlib算法进行压缩。
稍等一下,我将为您编写一些示例代码,以使用此类并将其公开到JavaScript中。 编辑 我已将文件上传至http://www.filefactory.com/file/cf8a39c/n/demo5.zip编辑2对于无法下载文件的人:
我的ActionScript代码在demo5.fla中(编译为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);
    }
}

以下是嵌入swf的HTML代码:

<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>

最后是JavaScript代码:
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];
    }
}

谢谢,我在等着。 :) 另外,使用Flash的文件处理容易吗?我猜想为了获取文件,您需要使用Flash的文件对话框,然后压缩它,最后以某种方式将其发送到服务器... - feketegy
@feketegy,我32分钟前添加了上传(不知道为什么之后添加的评论没有发布)。无论如何,其中的HTML包含相当自说明的JavaScript(带有注释),所以如果您不明白任何内容,请不要犹豫问。但是有一个问题:您需要在http://域上运行HTML或向Flash播放器添加异常(由您选择)。 - Pranav Hosangadi
谢谢,另外,我能否从<input type="file" />中传递文件到Flash?在JavaScript中,我只能访问文件的路径,什么都不能做。那么,一旦文件被压缩,您建议如何将其发送到服务器? - feketegy
啊!!!这个函数需要文件的内容,而不是路径。这是因为Flash(来自浏览器)无法读取文件,除非它是由用户交互触发的(比如点击或按键)。或者,您可以在Flash中创建一个完整的上传程序。我很快就会上传这样一个例子。 - Pranav Hosangadi
请参考http://livedocs.adobe.com/flex/3/html/help.html?content=17_Networking_and_communications_7.html,了解如何构建上传器(很抱歉我无法上传我的代码,因为时间紧迫)。这非常简单。 - Pranav Hosangadi

1

建议查看stackoverflow post。阅读两个回答可以很好地了解压缩现实情况。

我正在考虑实现一种Silverlight或Flex解决方案,该方案在客户端进行压缩,如果用户不想安装它,则在服务器端对文件进行压缩和解压缩。找到解决方案后会更新此帖子。

安装控件将被推销给用户作为节省时间的方法,这通常是正确的。对于服务器来说,这将是节省带宽和压缩处理的好方法。


1
使用 Silverlight,你可以在客户端上对文件进行压缩,并且这种方法适用于所有主要浏览器。此外,你可以通过 JavaScript 与你的 Silverlight 小部件进行交互。另外,如果用户需要上传多个文件,则你的 Silverlight 小部件可以显示一个选择所有文件的单个对话框。唯一的缺点是你的客户端必须安装 Silverlight 插件。

1

如果由于某种原因,您无法在所有主要浏览器中使用JavaScript解决方案,我知道这里有一个AS3压缩库:http://code.google.com/p/ascompress/

此外,如果您的目标用户在技术上有一定的了解,为什么不让他们上传一个xml的.zip文件呢?然后在服务器端,您可以根据需要进行解压缩和处理。

无论哪种方式,在服务器端,您都需要进行解压缩/解压缩,如果您还没有想到解决方案,那么应该很容易通过谷歌搜索找到解决方案。


感谢提供链接。这个解决方案有JavaScript API吗?我不太熟悉Flash/Actionscript(我只是一个用户)。此外,我不能要求用户事先压缩XML文件。不幸的是,这必须是一个自动化的过程,尽管那样会更理想... - feketegy
你可以在ActionScript中处理所有这些,因为它有自己的文件浏览器。但是,如果出于某种原因需要与JavaScript通信,你可以使用AS3的ExternalInterface。Pranav上面提供了一个不错的解决方案。 - ToddBFisher

0

有一些免费提供的哈夫曼压缩的JavaScript库,例如https://github.com/wilkerlucio/huffman_js,但我认为你的任务是不可能完成的,因为使用JavaScript和HTML无法将大量数据加载到浏览器或客户端内存中。


嘿,谢谢,但正如我在之前的评论中提到的,我需要一个适用于IE7+和其他主要浏览器的解决方案。这个解决方案有点像实验性的Javascript,我不相信它能处理超过40-50 MB的大型XML文件。 - feketegy
@feketegy:这只是一个建议。你可以自己去做。 - Micromega

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接