【问题标题】:How do I send file(s) via ajax without using html form?如何在不使用 html 表单的情况下通过 ajax 发送文件?
【发布时间】:2012-06-19 16:19:24
【问题描述】:

我在不使用 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
            }
        }
    );
    

但是服务器没有收到文件。 我已阅读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" />

【问题讨论】:

  • 请发布您在服务器上运行的代码。您应该能够从 POST 数组访问该文件。
  • @JamWaffles:已添加代码。

标签: javascript jquery ajax upload


【解决方案1】:

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

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

File 对象可以使用&lt;input type="file"&gt; 元素轻松检索,甚至可以从桌面拖放。

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

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

请记住,此时您必须在 Firefox 中使用 MozBlobBuilder 而在 Chrome 中使用 WebKitBlobBuilder

在一些教程中,我看到字符串实际上首先转换为Uint8Array。也许这是基于较旧的参考,因为BlobBuilderappend 方法也应该接受纯字符串。不过没试过。

如果您的内容是 Base64 编码的字符串,则必须使用 atob 对其进行转换(应该支持所有同时支持 BlobFormData 的浏览器)。

编辑:由于 Blob 构造函数的新草案,BlobBuilder 现在已被弃用。因此,获得Blob 所需要做的一切是:

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

剩下的就很简单了:

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

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

【讨论】:

  • 对于compatibility chart,我不太了解。你能解释一下吗?是的,我知道 的简单用法,但问题是:我想从用户输入的文本中发送文件,而不是从计算机中的现有文件中发送文件。
  • 您仍然需要FormData,但您可以开始解释 JSZip 的工作原理。它会创建一个文件吗?在哪里?本地?在服务器上?
  • JSZip 示例中,他们使用:var content = zip.generate(); location.href="data:application/zip;base64,"+content; 我认为“创建”文件位于内容变量中。客户端计算机中没有创建真正的文件。
  • 所以你只有一个base64字符串。这有点复杂,因为如果您 必须 发送文件而不是字符串,则必须先创建一个。目前唯一允许你这样做的浏览器是谷歌浏览器,它支持一个相当完整的 FileAPI 库。
  • ooucchh....所以目前无法伪造文件创建并通过 XHR 发送。您认为我应该将 textarea 内容作为纯字符串发送吗?这是一个很好的解决方法吗?
【解决方案2】:

我终于熬过去了!!非常感谢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);

【讨论】:

    猜你喜欢
    • 2015-07-25
    • 1970-01-01
    • 2015-09-29
    • 2012-07-24
    • 1970-01-01
    • 2018-09-18
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多