jQuery Ajax 文件上传

862

我可以使用以下jQuery代码通过ajax请求的POST方法执行文件上传吗?

$.ajax({
    type: "POST",
    timeout: 50000,
    url: url,
    data: dataString,
    success: function (data) {
        alert('success');
        return false;
    }
});

如果可能的话,我需要填写data部分吗?这是正确的方法吗?我只是将文件POST到服务器端。

我已经在谷歌上搜过了,但我发现的都是插件,而在我的计划中我不想使用它。至少现在不想。


Ajax不支持文件上传,您应该使用iframe。 - antyrat
1
相关问题:https://dev59.com/Emw05IYBdhLWcg3w3lol - Nathan Koop
2
相关链接:https://dev59.com/vXVC5IYBdhLWcg3w1E_w - Timo Huovinen
28个回答

640

通过 AJAX 上传文件是不可能的。
你可以使用IFrame在不刷新页面的情况下上传文件。
你可以在这里查看更多细节。


更新

使用 XHR2,可以支持通过 AJAX 上传文件。例如,可以通过 FormData 对象来实现,但遗憾的是不是所有/旧浏览器都支持它。

FormData 支持从以下桌面浏览器版本开始。

  • IE 10+
  • Firefox 4.0+
  • Chrome 7+
  • Safari 5+
  • Opera 12+

有关更多详细信息,请参见MDN链接


44
以下是一份不支持的特定浏览器列表:http://caniuse.com/#search=FormData。此外,我没有测试过,但这里有一个用于FormData的polyfill:https://gist.github.com/3120320。 - Ryan White
168
具体来说,对于那些懒得阅读链接的人来说,IE < 10 不支持。 - Kevin
26
@Synexis 不需要再等那么久了,因为IE的市场份额已经全球仅剩22%,在美国只有27%,并且正在迅速下降。很可能是70岁以上的人在使用IE浏览器。所以,与其由IE决定开发者该做什么,IE要么改进,要么退出浏览器竞争。 - Drew Calder
36
大多数使用IE的用户是办公室工作人员,由于公司政策,他们没有选择使用哪种浏览器。我认为年龄并不是决定因素。我猜想大多数70岁以上的人会让他们的后代安装Chrome或FF代替IE :) - Nicolas Connault
4
这个链接对我理解最基本的内容有很大帮助。我不需要使用 xhr 请求。如果你要使用 ajax,请确保将 enctype 设置为 "form/multipart" - Luminous
显示剩余5条评论

382

Iframes不再需要用于通过ajax上传文件。最近我自己完成了这个任务。查看以下页面:

使用HTML5文件上传和AJAX以及jQuery

http://dev.w3.org/2006/webapi/FileAPI/#FileReader-interface

更新了答案并进行了清理。使用getSize函数检查大小或使用getType函数检查类型。添加了进度条HTML和CSS代码。

var Upload = function (file) {
    this.file = file;
};

Upload.prototype.getType = function() {
    return this.file.type;
};
Upload.prototype.getSize = function() {
    return this.file.size;
};
Upload.prototype.getName = function() {
    return this.file.name;
};
Upload.prototype.doUpload = function () {
    var that = this;
    var formData = new FormData();

    // add assoc key values, this will be posts values
    formData.append("file", this.file, this.getName());
    formData.append("upload_file", true);

    $.ajax({
        type: "POST",
        url: "script",
        xhr: function () {
            var myXhr = $.ajaxSettings.xhr();
            if (myXhr.upload) {
                myXhr.upload.addEventListener('progress', that.progressHandling, false);
            }
            return myXhr;
        },
        success: function (data) {
            // your callback here
        },
        error: function (error) {
            // handle error
        },
        async: true,
        data: formData,
        cache: false,
        contentType: false,
        processData: false,
        timeout: 60000
    });
};

Upload.prototype.progressHandling = function (event) {
    var percent = 0;
    var position = event.loaded || event.position;
    var total = event.total;
    var progress_bar_id = "#progress-wrp";
    if (event.lengthComputable) {
        percent = Math.ceil(position / total * 100);
    }
    // update progressbars classes so it fits your code
    $(progress_bar_id + " .progress-bar").css("width", +percent + "%");
    $(progress_bar_id + " .status").text(percent + "%");
};

如何使用上传类
//Change id to your id
$("#ingredient_file").on("change", function (e) {
    var file = $(this)[0].files[0];
    var upload = new Upload(file);

    // maby check size or type here with upload.getSize() and upload.getType()

    // execute upload
    upload.doUpload();
});

进度条的HTML代码
<div id="progress-wrp">
    <div class="progress-bar"></div>
    <div class="status">0%</div>
</div>

进度条 CSS 代码
#progress-wrp {
  border: 1px solid #0099CC;
  padding: 1px;
  position: relative;
  height: 30px;
  border-radius: 3px;
  margin: 10px;
  text-align: left;
  background: #fff;
  box-shadow: inset 1px 3px 6px rgba(0, 0, 0, 0.12);
}

#progress-wrp .progress-bar {
  height: 100%;
  border-radius: 3px;
  background-color: #f39ac7;
  width: 0;
  box-shadow: inset 1px 1px 10px rgba(0, 0, 0, 0.11);
}

#progress-wrp .status {
  top: 3px;
  left: 50%;
  position: absolute;
  display: inline-block;
  color: #000000;
}

4
你可以直接复制代码并使用它,只需更改一些id名称和类名即可。任何自定义需求都需要自行完成。 - JohannesAndersson
4
请注意,myXhr、name、size和type似乎是全局变量。此外,最好使用"beforeSend"来增强已创建的XMLHttpRequest对象,而不是使用"xhr"来创建一个然后再改变它。 - awatts
8
@Ziinloader,我认为我们不能直接使用这个。你使用了一个未包含的本地方法:writer(catchFile)writer()是什么? - tandrewnichols
4
如果数据中除了要上传的文件外,还包含一些字段呢? - raju
5
@Ziinloader,我看到您多次回来并维护这个极其有用的示例。真正是一个非常有价值的答案,值得比我能给出的那个点赞更多的肯定。 - Regular Jo
显示剩余8条评论

261
Ajax可以进行post并上传文件。我使用jQuery $.ajax函数来加载我的文件。我曾尝试使用XHR对象,但在服务器端使用PHP时无法获得结果。
var formData = new FormData();
formData.append('file', $('#file')[0].files[0]);

$.ajax({
       url : 'upload.php',
       type : 'POST',
       data : formData,
       processData: false,  // tell jQuery not to process the data
       contentType: false,  // tell jQuery not to set contentType
       success : function(data) {
           console.log(data);
           alert(data);
       }
});

正如您所见,您必须创建一个FormData对象,可以是空的或者从现有表单中(序列化为$('#yourForm').serialize())创建,并附加输入文件。

这里有更多信息: - 如何使用 jQuery.ajax 和 FormData 上传文件 - 通过 jQuery 上传文件,提供了 FormData 对象但没有文件名,GET 请求

对于 PHP 处理,您可以使用以下代码:

//print_r($_FILES);
$fileName = $_FILES['file']['name'];
$fileType = $_FILES['file']['type'];
$fileError = $_FILES['file']['error'];
$fileContent = file_get_contents($_FILES['file']['tmp_name']);

if($fileError == UPLOAD_ERR_OK){
   //Processes your file here
}else{
   switch($fileError){
     case UPLOAD_ERR_INI_SIZE:   
          $message = 'Error al intentar subir un archivo que excede el tamaño permitido.';
          break;
     case UPLOAD_ERR_FORM_SIZE:  
          $message = 'Error al intentar subir un archivo que excede el tamaño permitido.';
          break;
     case UPLOAD_ERR_PARTIAL:    
          $message = 'Error: no terminó la acción de subir el archivo.';
          break;
     case UPLOAD_ERR_NO_FILE:    
          $message = 'Error: ningún archivo fue subido.';
          break;
     case UPLOAD_ERR_NO_TMP_DIR: 
          $message = 'Error: servidor no configurado para carga de archivos.';
          break;
     case UPLOAD_ERR_CANT_WRITE: 
          $message= 'Error: posible falla al grabar el archivo.';
          break;
     case  UPLOAD_ERR_EXTENSION: 
          $message = 'Error: carga de archivo no completada.';
          break;
     default: $message = 'Error: carga de archivo no completada.';
              break;
    }
      echo json_encode(array(
               'error' => true,
               'message' => $message
            ));
}

2
我需要引用哪个jQuery库才能运行这段代码? - Rayden Black
6
formData.append('file', $('#file')[0].files[0]);返回undefined,而console.log(formData)除了_proto_之外没有任何内容。 - Yakob Ubaidi
7
我成功地做到了......捏我一下,我正在享受jQuery Ajax文件上传的天堂! var formData = new FormData(); formData.append('file', document.getElementById('file').files[0]); $.ajax({ url : $("form[name='uploadPhoto']").attr("action"), type : 'POST', data : formData, processData: false, // 告诉jQuery不要处理数据 contentType: false, // 告诉jQuery不要设置contentType success : function(data) { console.log(data); alert(data); } }); - TARKUS
2
@RaymondWachaga 这是编码类型,而不是加密类型。 :) - Ted Bigham
2
我真的很苦恼这个问题... 经过数小时的研究等等,我找到了一个真正有用的东西。谢谢你,伙计!你解决方案的第一部分对我非常有效。那正是我所需要的 :-) - Damoiskii
显示剩余4条评论

135

简单的上传表单

<script>

   //form Submit
   $("form").submit(function(evt){   

      evt.preventDefault();
      var formData = new FormData($(this)[0]);

      $.ajax({
          url: 'fileUpload',
          type: 'POST',
          data: formData,
          async: false,
          cache: false,
          contentType: false,
          enctype: 'multipart/form-data',
          processData: false,
          success: function (response) {
         
             alert(response);
          }
       });

       return false;

    });

</script>
<!--Upload Form-->
<form>
  <table>
    <tr>
      <td colspan="2">File Upload</td>
    </tr>
    <tr>
      <th>Select File </th>
      <td><input id="csv" name="csv" type="file" /></td>
    </tr>
    <tr>
      <td colspan="2">
        <input type="submit" value="submit"/> 
      </td>
    </tr>
  </table>
</form>


先生,这个示例中使用了哪些JS?是否有特定的jQuery插件?我有一个问题,被指向了这里,请您帮忙查看一下我的问题。我想在该项目中上传多个文件或图像,这是链接:http://stackoverflow.com/questions/28644200/input-type-file-ajax-request?noredirect=1#comment45628551_28644200 - Brownman Revival
27
$(this)[0]this - machineaddict
2
服务器上发布文件的参数是什么?您能否发布服务器部分。 - FrenkyB
@FrenkyB 和其他人 - 服务器上的文件(在 PHP 中)不是存储在 $_POST 变量中的 - 它们存储在 $_FILES 变量中。在这种情况下,您可以使用 $_FILES["csv"] 访问它,因为 "csv" 是输入标记的名称属性。 - dev_masta
完全正确。 - Tejas Tank

74

我可能有些晚了,但我正在寻找一种基于ajax的图像上传解决方案,而我所寻找的答案在这篇文章中有点分散。我最终采用了包含FormData对象的解决方案。我整理了一段基本的代码示例,它演示了如何使用fd.append()向表单添加自定义字段以及在ajax请求完成时如何处理响应数据。

上传html:

<!DOCTYPE html>
<html>
<head>
    <title>Image Upload Form</title>
    <script src="//code.jquery.com/jquery-1.9.1.js"></script>
    <script type="text/javascript">
        function submitForm() {
            console.log("submit event");
            var fd = new FormData(document.getElementById("fileinfo"));
            fd.append("label", "WEBUPLOAD");
            $.ajax({
              url: "upload.php",
              type: "POST",
              data: fd,
              processData: false,  // tell jQuery not to process the data
              contentType: false   // tell jQuery not to set contentType
            }).done(function( data ) {
                console.log("PHP Output:");
                console.log( data );
            });
            return false;
        }
    </script>
</head>

<body>
    <form method="post" id="fileinfo" name="fileinfo" onsubmit="return submitForm();">
        <label>Select a file:</label><br>
        <input type="file" name="file" required />
        <input type="submit" value="Upload" />
    </form>
    <div id="output"></div>
</body>
</html>
在您使用PHP的情况下,以下是一种处理上传的方法,包括利用上述HTML中演示的两个自定义字段。
Upload.php
<?php
if ($_POST["label"]) {
    $label = $_POST["label"];
}
$allowedExts = array("gif", "jpeg", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp);
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/jpg")
|| ($_FILES["file"]["type"] == "image/pjpeg")
|| ($_FILES["file"]["type"] == "image/x-png")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 200000)
&& in_array($extension, $allowedExts)) {
    if ($_FILES["file"]["error"] > 0) {
        echo "Return Code: " . $_FILES["file"]["error"] . "<br>";
    } else {
        $filename = $label.$_FILES["file"]["name"];
        echo "Upload: " . $_FILES["file"]["name"] . "<br>";
        echo "Type: " . $_FILES["file"]["type"] . "<br>";
        echo "Size: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
        echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br>";

        if (file_exists("uploads/" . $filename)) {
            echo $filename . " already exists. ";
        } else {
            move_uploaded_file($_FILES["file"]["tmp_name"],
            "uploads/" . $filename);
            echo "Stored in: " . "uploads/" . $filename;
        }
    }
} else {
    echo "Invalid file";
}
?>

我收到了“跨源请求仅支持协议方案:http、data、chrome、chrome-extension、https”,为什么会这样,先生?我完全复制粘贴了您的代码。 - Brownman Revival
2
@HogRider - 如果你在Google上搜索你的错误信息,这是第一个结果:https://dev59.com/jWgv5IYBdhLWcg3wPOVn。你是通过`file://`本地访问网页吗?而不是使用Web服务器?另外,盲目地复制和粘贴代码而不先理解它并不是最佳实践。我建议你逐行阅读代码,以了解正在发生什么,然后再将代码应用到实际中。 - colincameron
@colincameron 感谢您澄清了一些事情,我已经逐行阅读了代码,但并没有完全理解,所以我提出了这个问题,希望有人能够解答我的疑惑。我正在使用本地的 XAMPP,可以问一个问题吗?也许您可以帮我澄清一下。 - Brownman Revival
@Brownman Revival:我知道回复晚了,你遇到了跨域错误,因为你是将HTML文件作为文件打开而不是从服务器运行它。 - Adarsh Mohan
在这段代码中,我如何根据选择应用表单操作? - Rayden Black
你必须从服务器打开HTML页面。 - Adarsh Mohan

35

24
很遗憾,IE浏览器版本低于10的不支持这个功能。 - Sasha Chedygov
1
当你只想将另一个页面作为答案参考时,你可以投票关闭为重复或在问题下留下评论。这篇文章不是答案。这种类型的文章看起来像是试图刷声望值。 - mickmackusa

29

使用纯JS更容易

async function saveFile(inp) 
{
    let formData = new FormData();           
    formData.append("file", inp.files[0]);
    await fetch('/upload/somedata', {method: "POST", body: formData});    
    alert('success');
}
<input type="file" onchange="saveFile(this)" >

  • 在服务器端,您可以读取自动包含在请求中的原始文件名(和其他信息)。
  • 您不需要设置标题“Content-Type”为“multipart/form-data”,浏览器会自动设置。
  • 此解决方案应适用于所有主要浏览器。

这是一个更多功能的片段,具有错误处理、超时和额外的JSON发送。

async function saveFile(inp) 
{
    let user = { name:'john', age:34 };
    let formData = new FormData();
    let photo = inp.files[0];      
         
    formData.append("photo", photo);
    formData.append("user", JSON.stringify(user));  
    
    const ctrl = new AbortController() // timeout
    setTimeout(() => ctrl.abort(), 50000);

    try {
       let r = await fetch('/upload/image', 
         {method: "POST", body: formData, signal: ctrl.signal}); 
       console.log('HTTP response code:',r.status); 
       alert('success');
    } catch(e) {
       console.log('Huston we have problem...:', e);
    }
    
}
<input type="file" onchange="saveFile(this)" >
<br><br>
Before selecting the file Open chrome console > network tab to see the request details.
<br><br>
<small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small>


最佳答案,而且代码更短。 - Said Torres

29

以下是我让它正常工作的方法:

HTML

<input type="file" id="file">
<button id='process-file-button'>Process</button>

JS

$('#process-file-button').on('click', function (e) {
    let files = new FormData(), // you can consider this as 'data bag'
        url = 'yourUrl';

    files.append('fileName', $('#file')[0].files[0]); // append selected file to the bag named 'file'

    $.ajax({
        type: 'post',
        url: url,
        processData: false,
        contentType: false,
        data: files,
        success: function (response) {
            console.log(response);
        },
        error: function (err) {
            console.log(err);
        }
    });
});

PHP

if (isset($_FILES) && !empty($_FILES)) {
    $file = $_FILES['fileName'];
    $name = $file['name'];
    $path = $file['tmp_name'];


    // process your file

}

3
对我最有帮助的是$('#file')[0].files[0],它是一种奇怪的JS解决方法,不需要使用正确的<form>表单。 - ffgpga08
2
这是完整的解决方案,PHP 部分也有所帮助。 - cdsaenz

20

使用FormData。它非常好用 :-) ...

var jform = new FormData();
jform.append('user',$('#user').val());
jform.append('image',$('#image').get(0).files[0]); // Here's the important bit

$.ajax({
    url: '/your-form-processing-page-url-here',
    type: 'POST',
    data: jform,
    dataType: 'json',
    mimeType: 'multipart/form-data', // this too
    contentType: false,
    cache: false,
    processData: false,
    success: function(data, status, jqXHR){
        alert('Hooray! All is well.');
        console.log(data);
        console.log(status);
        console.log(jqXHR);

    },
    error: function(jqXHR,status,error){
        // Hopefully we should never reach here
        console.log(jqXHR);
        console.log(status);
        console.log(error);
    }
});

这是什么:('user',$('#user').val()); - rahim.nagori
文本框的ID为“user”,已添加到表单中。@rahim.nagori - Alp Altunel
1
更直接的方法: var jform = new FormData($('form').get(0)); - André Morales
好的,谢谢。下次我使用FormData时会尝试这个方法。 - delboy1978uk

19

如果你想这样做:

$.upload( form.action, new FormData( myForm))
.progress( function( progressEvent, upload) {
    if( progressEvent.lengthComputable) {
        var percent = Math.round( progressEvent.loaded * 100 / progressEvent.total) + '%';
        if( upload) {
            console.log( percent + ' uploaded');
        } else {
            console.log( percent + ' downloaded');
        }
    }
})
.done( function() {
    console.log( 'Finished upload');                    
});

也许https://github.com/lgersman/jquery.orangevolt-ampere/blob/master/src/jquery.upload.js可以解决你的问题。


$对象中的上传方法在哪里,上面的链接不存在。 - coolesting
https://github.com/lgersman/jquery.orangevolt-ampere/blob/master/src/jquery.upload.js - lgersman
2
感谢您发布答案!请务必仔细阅读有关自我推广的FAQ。还请注意,每次链接到您自己的网站/产品时,必须发布免责声明。 - Andrew Barber

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