如何异步上传文件?

我想用 jQuery 异步上传文件。

$(document).ready(function () {
    $("#uploadbutton").click(function () {
        var filename = $("#file").val();

        $.ajax({
            type: "POST",
            url: "addFile.do",
            enctype: 'multipart/form-data',
            data: {
                file: filename
            },
            success: function () {
                alert("Data Uploaded: ");
            }
        });
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<span>File</span>
<input type="file" id="file" name="file" size="10"/>
<input id="uploadbutton" type="button" value="Upload"/>

我只获取文件名,而不是上传文件。我该怎么做才能解决此问题?

答案

使用HTML5,您可以使用 Ajax 和 jQuery 进行文件上传。不仅如此,您还可以执行文件验证(名称,大小和 MIME 类型)或使用 HTML5 进度标签(或 div)处理进度事件。最近,我不得不制作一个文件上传器,但是我不想使用Flash ,iframe 或插件,经过一番研究后,我提出了解决方案。

HTML:

<form enctype="multipart/form-data">
    <input name="file" type="file" />
    <input type="button" value="Upload" />
</form>
<progress></progress>

首先,您可以根据需要进行一些验证。例如,在文件的.on('change')事件中:

$(':file').on('change', function () {
  var file = this.files[0];

  if (file.size > 1024) {
    alert('max upload size is 1k');
  }

  // Also see .name, .type
});

现在,单击按钮即可提交$.ajax()

$(':button').on('click', function () {
  $.ajax({
    // Your server script to process the upload
    url: 'upload.php',
    type: 'POST',

    // Form data
    data: new FormData($('form')[0]),

    // Tell jQuery not to process data or worry about content-type
    // You *must* include these options!
    cache: false,
    contentType: false,
    processData: false,

    // Custom XMLHttpRequest
    xhr: function () {
      var myXhr = $.ajaxSettings.xhr();
      if (myXhr.upload) {
        // For handling the progress of the upload
        myXhr.upload.addEventListener('progress', function (e) {
          if (e.lengthComputable) {
            $('progress').attr({
              value: e.loaded,
              max: e.total,
            });
          }
        }, false);
      }
      return myXhr;
    }
  });
});

如您所见,借助 HTML5(和一些研究),文件上传不仅成为可能,而且超级容易。请尝试使用Google Chrome 浏览器,因为示例的某些 HTML5 组件并非在每种浏览器中都可用。

2019 更新:它仍然取决于您的人口统计使用的浏览器。

使用 “新” HTML5 file API 时要了解的重要一点是, 直到 IE 10 才支持它。如果您要针对的特定市场对旧版本 Windows 的倾向高于平均水平,则可能无法使用它。

截至 2017 年,大约 5%的浏览器是 IE 6、7、8 或 9 之一。如果您进入一家大公司(例如,这是 B2B 工具,或者您要提供培训的内容),那么这个数字可能会飙升。 2016 年,我与一家在其 60%以上的计算机上使用 IE8 的公司打交道。

截至本次编辑的 2019 年,距离我最初的回答差不多 11 年了。 IE9 及更低版本在全球范围内均在 1%左右,但仍存在使用率更高的集群。

最重要的外卖从这个 - whatever 的特征 - 是, 请检查您的用户使用的浏览器 。如果不这样做,您将学到一个快速而痛苦的课程,以了解为什么 “为我工作” 在交付给客户的产品中不够好。 caniuse是一个有用的工具,但请注意他们从哪里获得人口统计信息。它们可能与您的不一致。这从未比企业环境更真实。

我在 2008 年的回答如下。


但是,有可行的非 JS 文件上传方法。您可以在页面上创建一个 iframe(使用 CSS 隐藏该 iframe),然后将表单定位为发布到该 iframe。主页不需要移动。

这是一个 “真实” 的帖子,因此并不完全是交互式的。如果您需要状态,则需要服务器端进行处理。具体取决于您的服务器。 ASP.NET具有更好的机制。 PHP 普通失败,但是您可以使用Perl或 Apache 修改来解决它。

如果您需要多个文件上传,则最好一次执行一个文件(以克服最大文件上传限制)。将第一个表单发布到 iframe 中,使用上述方法监视进度,完成后,将第二个表单发布到 iframe 中,依此类推。

我建议为此使用Fine Uploader插件。您的JavaScript代码为:

$(document).ready(function() {
  $("#uploadbutton").jsupload({
    action: "addFile.do",
    onComplete: function(response){
      alert( "server response: " + response);
    }
  });
});

注意:此答案已过时,现在可以使用 XHR 上传文件。


您不能使用XMLHttpRequest (Ajax)上传文件。您可以使用 iframe 或 Flash 模拟效果。出色的jQuery 表单插件 ,可通过 iframe 发布文件以获得效果。

为将来的读者做准备。

异步文件上传

使用 HTML5

如果支持FormDataFile API (均具有 HTML5 功能),则可以使用$.ajax()方法使用jQuery上传文件。

您也可以发送不带 FormData 的文件但是必须以两种方式发送文件 API 来处理文件,以便可以使用XMLHttpRequest (Ajax)发送文件。

$.ajax({
  url: 'file/destination.html', 
  type: 'POST',
  data: new FormData($('#formWithFiles')[0]), // The form with the file inputs.
  processData: false,
  contentType: false                    // Using FormData, no need to process data.
}).done(function(){
  console.log("Success: Files sent!");
}).fail(function(){
  console.log("An error occurred, the files couldn't be sent!");
});

有关快速的纯 JavaScript( 无 jQuery )示例,请参见 “ 使用 FormData 对象发送文件 ”。

倒退

当不支持 HTML5(无File API )时,唯一的其他纯 JavaScript 解决方案(无Flash或任何其他浏览器插件)是隐藏的 iframe技术,该技术允许在不使用XMLHttpRequest对象的情况下模拟异步请求。

它包括通过输入文件将 iframe 设置为表单的目标。当用户提交请求并上传文件时,响应会显示在 iframe 中,而不是重新呈现主页。隐藏 iframe 可使整个过程对用户透明,并模拟异步请求。

如果做得正确,它实际上可以在任何浏览器上运行,但是在从 iframe 获取响应方面有一些警告。

在这种情况下,您可能更喜欢使用Bifröst 之类的包装器插件,该插件使用iframe 技术,但还提供了jQuery Ajax 传输,允许仅使用$.ajax()方法发送文件 ,如下所示:

$.ajax({
  url: 'file/destination.html', 
  type: 'POST',
  // Set the transport to use (iframe means to use Bifröst)
  // and the expected data type (json in this case).
  dataType: 'iframe json',                                
  fileInputs: $('input[type="file"]'),  // The file inputs containing the files to send.
  data: { msg: 'Some extra data you might need.'}
}).done(function(){
  console.log("Success: Files sent!");
}).fail(function(){
  console.log("An error occurred, the files couldn't be sent!");
});

外挂程式

Bifröst只是一个小的包装,它为 jQuery 的 ajax 方法添加了后备支持,但是许多上述插件,例如jQuery Form PluginjQuery File Upload,都包括从 HTML5 到不同后备的整个堆栈,以及一些简化该过程的有用功能。根据您的需求和要求,您可能需要考虑一个简单的实现或两个插件之一。

这个AJAX 文件上传 jQuery 插件以某种方式上传文件,并将响应传递给回调,仅此而已。

  • 它不依赖于特定的 HTML,只需为其提供<input type="file">
  • 它不需要您的服务器以任何特定方式响应
  • 使用多少个文件或它们在页面上的位置都没有关系

- 少用 -

$('#one-specific-file').ajaxfileupload({
  'action': '/upload.php'
});

- 或多达 -

$('input[type="file"]').ajaxfileupload({
  'action': '/upload.php',
  'params': {
    'extra': 'info'
  },
  'onComplete': function(response) {
    console.log('custom handler for file:');
    alert(JSON.stringify(response));
  },
  'onStart': function() {
    if(weWantedTo) return false; // cancels upload
  },
  'onCancel': function() {
    console.log('no file selected');
  }
});

我一直在使用下面的脚本上传图像,但效果很好。

的 HTML

<input id="file" type="file" name="file"/>
<div id="response"></div>

的 JavaScript

jQuery('document').ready(function(){
    var input = document.getElementById("file");
    var formdata = false;
    if (window.FormData) {
        formdata = new FormData();
    }
    input.addEventListener("change", function (evt) {
        var i = 0, len = this.files.length, img, reader, file;

        for ( ; i < len; i++ ) {
            file = this.files[i];

            if (!!file.type.match(/image.*/)) {
                if ( window.FileReader ) {
                    reader = new FileReader();
                    reader.onloadend = function (e) {
                        //showUploadedItem(e.target.result, file.fileName);
                    };
                    reader.readAsDataURL(file);
                }

                if (formdata) {
                    formdata.append("image", file);
                    formdata.append("extra",'extra-data');
                }

                if (formdata) {
                    jQuery('div#response').html('<br /><img src="ajax-loader.gif"/>');

                    jQuery.ajax({
                        url: "upload.php",
                        type: "POST",
                        data: formdata,
                        processData: false,
                        contentType: false,
                        success: function (res) {
                         jQuery('div#response').html("Successfully uploaded");
                        }
                    });
                }
            }
            else
            {
                alert('Not a vaild image!');
            }
        }

    }, false);
});

说明

我使用 response div来显示上传动画和上传完成后的响应。

最好的部分是,使用此脚本时,您可以随文件发送额外的数据,例如 ids 等。我已经提到了脚本中的extra-data

在 PHP 级别,这将像正常文件上传一样工作。可以将多余数据作为$_POST数据进行检索。

在这里,您没有使用插件和东西。您可以根据需要更改代码。您在这里不是盲目编码。这是任何 jQuery 文件上传的核心功能。其实是 Javascript。

您可以非常轻松地在原始 JavaScript 中完成此操作。这是我当前项目的摘录:

var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
    var percent = (e.position/ e.totalSize);
    // Render a pretty progress bar
};
xhr.onreadystatechange = function(e) {
    if(this.readyState === 4) {
        // Handle file upload complete
    }
};
xhr.open('POST', '/upload', true);
xhr.setRequestHeader('X-FileName',file.name); // Pass the filename along
xhr.send(file);

您可以仅使用 jQuery .ajax()上传。

HTML:

<form id="upload-form">
    <div>
        <label for="file">File:</label>
        <input type="file" id="file" name="file" />
        <progress class="progress" value="0" max="100"></progress>
    </div>
    <hr />
    <input type="submit" value="Submit" />
</form>

的 CSS

.progress { display: none; }

Javascript:

$(document).ready(function(ev) {
    $("#upload-form").on('submit', (function(ev) {
        ev.preventDefault();
        $.ajax({
            xhr: function() {
                var progress = $('.progress'),
                    xhr = $.ajaxSettings.xhr();

                progress.show();

                xhr.upload.onprogress = function(ev) {
                    if (ev.lengthComputable) {
                        var percentComplete = parseInt((ev.loaded / ev.total) * 100);
                        progress.val(percentComplete);
                        if (percentComplete === 100) {
                            progress.hide().val(0);
                        }
                    }
                };

                return xhr;
            },
            url: 'upload.php',
            type: 'POST',
            data: new FormData(this),
            contentType: false,
            cache: false,
            processData: false,
            success: function(data, status, xhr) {
                // ...
            },
            error: function(xhr, status, error) {
                // ...
            }
       });
    }));
});

我过去做过的最简单,最可靠的方法是,仅将表单指向一个隐藏的 iFrame 标签 - 然后它将在 iframe 中提交而无需重新加载页面。

也就是说,如果您不想使用插件,JavaScript 或 HTML 以外的任何其他形式的 “魔术”。当然,您可以将其与 JavaScript 结合使用,或者您拥有什么...

<form target="iframe" action="" method="post" enctype="multipart/form-data">
    <input name="file" type="file" />
    <input type="button" value="Upload" />
</form>

<iframe name="iframe" id="iframe" style="display:none" ></iframe>

您还可以读取 iframe onLoad的内容以获取服务器错误或成功响应,然后将其输出给用户。

Chrome,iFrame 和 onLoad

- 注意 - 如果您对上载 / 下载时如何设置 UI 阻止程序感兴趣,则只需要继续阅读

当前,Chrome 在用于传输文件时不会触发 iframe 的 onLoad 事件。 Firefox,IE 和 Edge 均会触发 onload 事件以进行文件传输。

我发现适用于 Chrome 的唯一解决方案是使用 Cookie。

要在开始上传 / 下载时基本上做到这一点:

  • [客户端] 开始间隔以查找 cookie 的存在
  • [服务器端] 对文件数据进行任何所需的操作
  • [服务器端] 设置客户端间隔的 cookie
  • [客户端] Interval 看到 cookie 并像 onLoad 事件一样使用它。例如,您可以启动 UI 阻止程序,然后 onLoad(或制作 cookie 时)删除 UI 阻止程序。

为此使用 cookie 是很丑陋的,但是可以。

下载时,我制作了一个 jQuery 插件来处理 Chrome 的此问题,您可以在此处找到

https://github.com/ArtisticPhoenix/jQuery-Plugins/blob/master/iDownloader.js

相同的基本原理也适用于上传。

使用下载器(显然包括 JS)

$('body').iDownloader({
     "onComplete" : function(){
          $('#uiBlocker').css('display', 'none'); //hide ui blocker on complete
     }
 });

 $('somebuttion').click( function(){
      $('#uiBlocker').css('display', 'block'); //block the UI
      $('body').iDownloader('download', 'htttp://example.com/location/of/download');
 });

在服务器端,就在传输文件数据之前,创建 cookie

setcookie('iDownloader', true, time() + 30, "/");

插件将看到 cookie,然后触发onComplete回调。