2

我在不使用 html 表单的情况下通过 ajax 上传文件时遇到了困难。我的情况是这样的:

  1. 我有一个文本区域
  2. 我想压缩该文本区域的内容并通过 AJAX 将其上传到服务器(现在我正在使用JSZip
  3. 出于测试目的,我尝试创建一个虚拟 zip 文件以像这样发送(不获取 textarea 内容):

    var zip = new JSZip();
    zip.file("hello1.txt", "Hello First World\n");<br/>
    zip.file("hello2.txt", "Hello Second World\n");<br/>
    var content = zip.generate();
    
  4. 然后我使用Jquery ajax 方法发送,像这样:

    $.post("the_url",
        {
            GradeRequest : {
                submitter_id : "foobar",
                evaluationset_id : 9,
                mode : 1,
                source_file : "a.cpp",
                file: content
            }
        }
    );
    

但是服务器上没有收到文件。我已阅读如何异步上传文件中的问题?,但所有解决方案都使用 html 表单。有没有不涉及任何 html 表单的解决方案?

提前致谢。

在服务器端,我使用的是 Yii PHP 框架:

$model = $this->model;
    if ($model !== null && isset($_POST['GradeRequest'])) {
        $model->setAttributes($_POST['GradeRequest']);

        if ($model->validate()) {
            if (isset($_FILES['GradeRequest']['tmp_name']['file']) && $_FILES['GradeRequest']['tmp_name']['file'] !== "") {
                $model->file = file_get_contents($_FILES['GradeRequest']['tmp_name']['file']);
                $model->source_file = $_FILES['GradeRequest']['name']['file'];
            }

            $this->setReply(true, "Ok");
            $model->client_id = $this->clientId;
            $model->request_id = $this->requestRecord->id;
            $model->save();
        } else {
            $this->setReply(false, $model->getErrors());
        }
    }

作为概念证明,我创建了一个这样的表单版本并且它可以工作(文件已上传到服务器):

<form enctype="multipart/form-data" method="post" action="[the_url]>
EvaluationSet id: <input type="text" name="GradeRequest[evaluationset_id]" /><br />
<input type="hidden" name="GradeRequest[mode]" value="0" />
<input type="hidden" name="GradeRequest[submitter_id]" value="Someone" />

Source file : <input type="text" name="GradeRequest[source_file]" /><br />
File : <input type="file" name="GradeRequest[file]" /><br />

<input type="submit" />

4

2 回答 2

3

那里FormData可以帮助您的对象(它是所谓的xmlHttpRequest版本 2 的一部分):这是一个兼容性图表,带有一些链接的参考和示例。

这样,您可以将键/值对添加到 POST 表单,包括File对象,并send通过xmlHttpRequest.

File可以使用<input type="file">元素轻松检索对象,甚至可以使用桌面上的拖放操作。

如果要将某些文件内容作为文件上传,则必须创建一个Blob. 此功能仍处于试验阶段,但受 Chrome 和 Firefox 支持(至少......我猜还有 Safari?):

var builder = new BlobBuilder();
builder.append(content);
var blob = builder.getBlob("application/zip");

请记住,此时您必须MozBlobBuilder在 Firefox 和WebKitBlobBuilderChrome 中使用。

在一些教程中,我看到字符串实际上被转换为Uint8Array第一个。也许那是基于较旧的参考,因为该append方法也BlobBuilder应该接受纯字符串。虽然从未尝试过。

如果您的内容是 Base64 编码的字符串,则必须使用atob(应该支持每个也支持Bloband的浏览器FormData) 对其进行转换。

编辑BlobBuilder由于 Blob 构造函数的新草案,现在已弃用。所以你必须做的一切Blob是:

var blob = new Blob([content], "application/zip");

剩下的很简单:

var form = new FormData();
form.append("file", blob);

这里的问题是服务器端的文件名是不可预测的,并且依赖于用户代理。我已经看到了一些使用append指定文件名的第三个参数的用法,但我想将实际文件名发送到FormData对象中的单独键/值对是个好主意。

于 2012-06-17T09:42:57.673 回答
1

我终于熬过去了!!非常感谢MaxArt

基本上,这就是MaxArt所说的 Uint8Array 转换。我在这里得到了 Eric Bidelman 的转换参考。

这是我的做法:

var zip = new JSZip();
zip.file("hello1.txt", "Hello First World\n");
zip.file("hello2.txt", "Hello Second World\n");
var content = zip.generate(); //Generate dummy zip file (adjust to your need)

var oBlob = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)(); //Instantiate blob builder

var raw = atob(content);    //decode the base64 string
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) { //convert to uInt8Array
    uInt8Array[i] = raw.charCodeAt(i);
}

oBlob.append(uInt8Array.buffer); //append it to blobbuilder
oMyForm.append("GradeRequest[file]", oBlob.getBlob("application/zip")); //because you create a zip file, so get the zip type

//send it
var oReq = new XMLHttpRequest();
oReq.open("POST", "{{the_url}}");
oReq.send(oMyForm);
于 2012-06-17T17:59:30.140 回答