如何在不使用html表单的情况下通过ajax发送文件?

时间:2012-06-17 09:29:13

标签: javascript jquery ajax upload

我遇到了通过ajax上传文件而没有单独的html表单的困难。 我的情况是这样的:

  1. 我有一个textarea
  2. 我想压缩该textarea的内容并通过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
            }
        }
    );
    
  5. 但是服务器上没有收到该文件。 我在How can I upload files asynchronously?中已阅读过问题,但所有解决方案都使用的是html格式。是否有任何解决方案不涉及任何HTML格式?

    提前致谢。

    在服务器端,我正在使用Yii PHP Framework:

    $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" />
    

2 个答案:

答案 0 :(得分:3)

FormData对象可以帮助你(它是所谓的xmlHttpRequest版本2的一部分):这是一个compatibility chart,带有一些链接的引用和示例。

这样,您就可以为POST表单添加键/值对,包括File个对象,并通过send的常用xmlHttpRequest方法将其全部发送。

可以使用File元素轻松检索

<input type="file">个对象,甚至可以使用drag&amp;从桌面上删除。

如果您想将某个文件内容上传为文件,则必须创建Blob。这个功能仍然是实验性的,但Chrome和Firefox支持(至少......和Safari我猜?):

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

请注意,目前您必须在Firefox中使用MozBlobBuilder,而在Chrome中使用WebKitBlobBuilder

在一些教程中,我看到字符串实际上首先转换为Uint8Array。也许这是基于较旧的引用,因为append的{​​{1}}方法也应该接受普通字符串。从来没有尝试过。

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

由于Blob构造函数的新草稿,

编辑FormData现已弃用。因此,获取BlobBuilder所需要做的就是:

Blob

其余的很简单:

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

这里的问题是服务器端的文件名是不可预测的,并且取决于用户代理。我已经看到var form = new FormData(); form.append("file", blob); 的一些用法以及指定文件名的第三个参数,但我想将实际文件名发送到append对象中的单独键/值对是个好主意。

答案 1 :(得分:1)

我终于明白了!!非常感谢MaxArt

基本上,这是MaxArt对Uint8Array转换的说法。我从Eric Bidelman here获得了转换参考。

以下是我的表现:

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