如何使用JavaScript上传Blob?

132

我有一个以这种结构存储的二进制数据:

Blob {type: "audio/wav", size: 655404, slice: function}
size: 655404
type: "audio/wav"
__proto__: Blob

这实际上是使用最近的Chrome getUerMedia()Recorder.js记录的声音数据。

我该如何使用 jQuery 的 post 方法将此 Blob 上传到服务器? 我已经尝试过以下方法但没有成功:

   $.post('http://localhost/upload.php', { fname: "test.wav", data: soundBlob }, 
    function(responseText) {
           console.log(responseText);
    });

1
你可能想考虑使用binaryJS来完成这个任务。如果你需要流式传输数据,那么它可能会有所帮助。 - toxicate20
这个答案也非常详细。https://dev59.com/vXVC5IYBdhLWcg3w1E_w#8758614 - Fabrício Matté
6个回答

146
你可以使用FormData API。如果你正在使用jquery.ajax,则需要设置processData: falsecontentType: false
var fd = new FormData();
fd.append('fname', 'test.wav');
fd.append('data', soundBlob);
$.ajax({
    type: 'POST',
    url: '/upload.php',
    data: fd,
    processData: false,
    contentType: false
}).done(function(data) {
       console.log(data);
});

3
你知道如何不使用AJAX来实现这个吗? - William Entriken
1
@FullDecent 你的意思是什么?是使用文件API提示用户下载文件吗?还是只是存储Blob内容? - Fabrício Matté
4
基本上要做的是 $('input[type=file]').value=blob。该代码的意思是将变量 "blob" 的值赋给一个 input 标签中 type 属性为 file 的元素。 - William Entriken
16
安全要求防止程序化设置文件输入的值:https://dev59.com/x3I-5IYBdhLWcg3wu7Pv - yeeking
9
请注意,与文件不同,当将Blob发送到服务器时,它具有通用文件名。但是你可以在FormData中指定Blob的文件名:https://dev59.com/rmw15IYBdhLWcg3wWqf- - Sebastien Lorber
显示剩余3条评论

58

2019 更新

此更新使用最新的Fetch API,无需使用 jQuery。

免责声明:不适用于 IE、Opera Mini 和旧版浏览器。请参见caniuse

基本 Fetch

它可以非常简单:

  fetch(`https://example.com/upload.php`, {method:"POST", body:blobData})
                .then(response => console.log(response.text()))

带有错误处理的获取数据

添加错误处理后,代码可能如下所示:

fetch(`https://example.com/upload.php`, {method:"POST", body:blobData})
            .then(response => {
                if (response.ok) return response;
                else throw Error(`Server returned ${response.status}: ${response.statusText}`)
            })
            .then(response => console.log(response.text()))
            .catch(err => {
                alert(err);
            });

PHP代码

这是upload.php文件中的服务器端代码。

<?php    
    // gets entire POST body
    $data = file_get_contents('php://input');
    // write the data out to the file
    $fp = fopen("path/to/file", "wb");

    fwrite($fp, $data);
    fclose($fp);
?>

1
最佳回答使用了jQuery,比这里的错误处理版本还要复杂。它有126个赞。另一方面,这个解决方案只有11(现在12)个赞,并且能够原生地使用js,非常简短。我希望我有120个赞可以将这个解决方案放在应该出现的位置。 - Stephen Duffy
1
@StephenDuffy 答案相隔2年。 - PauAI
工作得很好,但可能会打开一些安全漏洞。然而,这确实是一个非常好的训练,可以理解为什么会出现这种情况。 - NVRM

23

实际上,您不必使用FormData从JavaScript将Blob发送到服务器(而File也是一个Blob)。

jQuery示例:

var file = $('#fileInput').get(0).files.item(0); // instance of File
$.ajax({
  type: 'POST',
  url: 'upload.php',
  data: file,
  contentType: 'application/my-binary-type', // set accordingly
  processData: false
});

纯JavaScript示例:

var file = $('#fileInput').get(0).files.item(0); // instance of File
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload.php', true);
xhr.onload = function(e) { ... };
xhr.send(file);

如果您要将传统的HTML多部分表单替换为“AJAX”实现(即,您的后端使用多部分表单数据),则应像另一个答案中所述使用 FormData 对象。

来源:XMLHttpRequest2的新技巧| HTML5 Rocks


19

我无法通过使用blob让上面的例子正常工作,我想知道upload.php文件中确切的内容。以下是内容:

(仅在Chrome 28.0.1500.95中测试过)

// javascript function that uploads a blob to upload.php
function uploadBlob(){
    // create a blob here for testing
    var blob = new Blob(["i am a blob"]);
    //var blob = yourAudioBlobCapturedFromWebAudioAPI;// for example   
    var reader = new FileReader();
    // this function is triggered once a call to readAsDataURL returns
    reader.onload = function(event){
        var fd = new FormData();
        fd.append('fname', 'test.txt');
        fd.append('data', event.target.result);
        $.ajax({
            type: 'POST',
            url: 'upload.php',
            data: fd,
            processData: false,
            contentType: false
        }).done(function(data) {
            // print the output from the upload.php script
            console.log(data);
        });
    };      
    // trigger the read from the reader...
    reader.readAsDataURL(blob);

}

upload.php的内容:

<?
// pull the raw binary data from the POST array
$data = substr($_POST['data'], strpos($_POST['data'], ",") + 1);
// decode it
$decodedData = base64_decode($data);
// print out the raw data, 
echo ($decodedData);
$filename = "test.txt";
// write the data out to the file
$fp = fopen($filename, 'wb');
fwrite($fp, $decodedData);
fclose($fp);
?>

我非常确定你可以将ajax函数调用中的行data: fd,更改为data: blob, - Kenny Evitt

12

我能够通过使用JavaScript对象传输Blob而不是使用FormData来使@yeeking的示例工作。它可以与使用recorder.js创建的音频blob一起工作。在Chrome版本32.0.1700.107中进行了测试。

function uploadAudio( blob ) {
  var reader = new FileReader();
  reader.onload = function(event){
    var fd = {};
    fd["fname"] = "test.wav";
    fd["data"] = event.target.result;
    $.ajax({
      type: 'POST',
      url: 'upload.php',
      data: fd,
      dataType: 'text'
    }).done(function(data) {
        console.log(data);
    });
  };
  reader.readAsDataURL(blob);
}

上传.php的内容

<?
// pull the raw binary data from the POST array
$data = substr($_POST['data'], strpos($_POST['data'], ",") + 1);
// decode it
$decodedData = base64_decode($data);
// print out the raw data,
$filename = $_POST['fname'];
echo $filename;
// write the data out to the file
$fp = fopen($filename, 'wb');
fwrite($fp, $decodedData);
fclose($fp);
?>

9
在 PHP 文件中要小心 - 如果允许 HTTP 客户端设置文件名,他们可能会使用它来将恶意内容上传到他们选择的某个文件和目录中(只要 Apache 有写入权限)。 - yeeking

2
我尝试了以上所有解决方案,还尝试了相关答案中的解决方案。这些解决方案包括但不限于将blob手动传递给HTMLInputElement的文件属性,调用FileReader的所有readAs*方法,使用File实例作为FormData.append调用的第二个参数,尝试通过获取URL.createObjectURL(myBlob)的值来将blob数据作为字符串获取,但结果很糟糕,并且导致我的计算机崩溃。

现在,如果你尝试了这些或更多的解决方案,仍然发现无法上传你的blob,那么问题可能是服务器端的。在我的情况下,我的blob超过了PHP.INI中的http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize和post_max_size限制,因此该文件离开前端表单,但被服务器拒绝。你可以直接在PHP.INI中增加这个值,或者通过.htaccess文件进行增加。


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