使用XMLHttprequest上传文件 - multipart/form-data中缺少边界

14

我正在使用XMLHttprequest上传文件。 这是上传文件的JavaScript函数:

var upload = function(file) {
    // Create form data
    var formData = new FormData();
    formData.append('file', file);

    var xhr = new XMLHttpRequest();

    // Open
    xhr.open('POST', this.options.action);

    // Set headers
    xhr.setRequestHeader("Cache-Control", "no-cache");
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.setRequestHeader("Content-Type", "multipart/form-data");
    xhr.setRequestHeader("X-File-Name", file.fileName);
    xhr.setRequestHeader("X-File-Size", file.fileSize);
    xhr.setRequestHeader("X-File-Type", file.type);

    // Send
    xhr.send(formData);
}

在服务器端,我通过这种方式读取文件:upload.php

file_put_contents($filename, (file_get_contents('php://input')));

除了我收到一个 PHP 警告之外,一切都正常:

Missing boundary in multipart/form-data POST data in Unknown on line 0

如果我删除这一行:xhr.setRequestHeader("Content-Type", "multipart/form-data"); 警告就会消失。

这里可能出了什么问题呢?


2
尝试使用application/x-www-form-urlencoded作为内容类型。或者可以省略它,因为这是由ajax请求发送的默认内容类型。 - Elias Van Ootegem
1
这个方法可以运行,但对于大文件(甚至5-600 KB),浏览器会冻结,因为它必须以“文本”格式发送文件。我必须使用multipart/form data - Tamás Pap
3
抱歉,我刚才忽略了文件上传的部分(不知道怎么回事),但是我认为这可能回答了您的问题,具体来说:xhr.setRequestHeader("content-type","multipart/form-data; charset=utf-8; boundary=" + Math.random().toString().substr(2)); 应该可以解决问题。 - Elias Van Ootegem
也尝试了这个解决方案。警告消失了,但是在这种情况下没有数据传输:file_get_contents('php://input')为空。非常感谢您的帮助Elias,如果您有其他想法,请告诉我。 - Tamás Pap
我认为问题在于您的“Content-Type”头中缺少边界。它应该类似于:Content-Type: multipart/form-data; boundary="same-as-the-boundary-in-request-body"如果您省略设置“Content-Type”头,则通常会为您创建此内容。 - Alexis Kalyvitis
@TamásPap:如果你删除所有的头文件代码,上传后的文件中是否会添加类似于“------WebKitFormBoundaryklb8qUzyCQJSI440 Content-Disposition: form-data; name="file"”这样的内容?我有这些额外的数据,它让我很困扰,因为它会在传输过程中改变文件的sha1值。 - Ng2-Fun
1个回答

20

这对我来说有点奇怪,但它确实有效:

// Open
xhr.open('POST', this.options.action, true);

// !!! REMOVED ALL HEADERS

// Send
xhr.send(formData);
在这种情况下,服务器端我不会从php://input读取发送的文件,而是文件将在$_FILES数组中。这解决了我的问题,但我仍然好奇为什么现在出现在$_FILES中?已在Chrome、Mozilla、Safari和IE10上进行了测试。

13
如果您没有手动指定任何内容,规范解释了(第3点)浏览器会设置正确的标头(包括Content-Type中正确的多部分边界指示)。该文件出现在$_FILES中,因为PHP 自动将多部分表单上传的文件放入该超级全局变量中 - Gijs
1
我不得不将这行代码注释掉才能使其工作:xhr.setRequestHeader('Content-Type', 'multipart/form-data');......(但保留了xhr.setRequestHeader('Accept', 'multipart/form-data'))。 - MCH

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