Как я могу загружать файлы асинхронно? - PullRequest
2765 голосов
/ 03 октября 2008

Я хотел бы загрузить файл асинхронно с jQuery. Это мой HTML:

<span>File</span>
<input type="file" id="file" name="file" size="10"/>
<input id="uploadbutton" type="button" value="Upload"/>

А вот мой 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: ");
            }
        });
    });
});

Вместо загружаемого файла я получаю только имя файла. Что я могу сделать, чтобы решить эту проблему?

Текущее решение

Я использую плагин jQuery Form для загрузки файлов.

Ответы [ 32 ]

2443 голосов
/ 06 января 2012

С HTML5 вы можете загружать файлы с помощью Ajax и jQuery. Кроме того, вы можете выполнять проверку файлов (имя, размер и тип MIME) или обрабатывать событие progress с помощью тега прогресса HTML5 (или div). Недавно мне пришлось сделать загрузчик файлов, но я не хотел использовать Flash , ни Iframes или плагины, и после некоторых исследований я нашел решение.

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 примеров доступны не во всех браузерах.

254 голосов
/ 03 октября 2008

2019 Обновление: оно все еще зависит от браузеров ваше демографическое использование.

Важная вещь, которую нужно понять с помощью "нового" API HTML5 file, заключается в том, что не поддерживается до IE 10 . Если конкретный рынок, на который вы ориентируетесь, имеет склонность выше средних к старым версиям Windows, у вас может не быть к нему доступа.

По состоянию на 2017 год около 5% браузеров - это IE 6, 7, 8 или 9. Если вы идете в большую корпорацию (например, это инструмент B2B или то, что вы предоставляете для обучения), то это число может ракета. В 2016 году я работал с компанией, использующей IE8, на более чем 60% своих машин.

Сейчас это редактирование 2019 года, почти через 11 лет после моего первоначального ответа. IE9 и ниже составляют глобально около отметки 1%, но все еще существуют кластеры с более высоким уровнем использования.

Важный вывод - независимо от того, какая функция - проверяет, какой браузер ваши пользователи используют . Если вы этого не сделаете, вы извлечете быстрый и мучительный урок о том, почему «работает для меня» недостаточно хорошо в доставке клиенту. caniuse - полезный инструмент, но обратите внимание, откуда они берут свою демографию. Они могут не совпадать с вашими. Это никогда не бывает правдой, чем корпоративная среда.

Ниже следует мой ответ за 2008 год.


Тем не менее, существуют жизнеспособные не-JS методы загрузки файлов. Вы можете создать на странице iframe (который вы скрываете с помощью CSS), а затем настроить таргетинг своей формы для публикации на этом iframe. Главная страница не нуждается в перемещении.

Это «реальная» запись, поэтому она не является полностью интерактивной. Если вам нужен статус, вам нужно что-то на стороне сервера для обработки этого. Это сильно зависит от вашего сервера. ASP.NET имеет более приятные механизмы. PHP plain не работает, но вы можете использовать Perl или модификации Apache, чтобы обойти это.

Если вам нужно несколько загрузок файлов, лучше делать каждый файл по одному (чтобы преодолеть максимальные ограничения на загрузку файлов). Отправьте первую форму в iframe, следите за ее продвижением, используя приведенное выше, а после ее завершения отправьте вторую форму в iframe и т. Д.

Или используйте решение Java / Flash. Они намного более гибки в том, что они могут делать со своими постами ...

109 голосов
/ 21 ноября 2008

Я рекомендую использовать для этой цели плагин Fine Uploader . Ваш JavaScript код будет:

$(document).ready(function() {
  $("#uploadbutton").jsupload({
    action: "addFile.do",
    onComplete: function(response){
      alert( "server response: " + response);
    }
  });
});
94 голосов
/ 03 октября 2008

Примечание. Этот ответ устарел, теперь можно загружать файлы с использованием XHR.


Вы не можете загружать файлы, используя XMLHttpRequest (Ajax). Вы можете имитировать эффект, используя iframe или Flash. Отличный jQuery Form Plugin , который отправляет ваши файлы через iframe, чтобы получить эффект.

87 голосов
/ 25 августа 2014

Подведение итогов для будущих читателей.

Асинхронная загрузка файлов

С HTML5

Вы можете загружать файлы с помощью jQuery , используя метод $.ajax(), если поддерживаются FormData и File API (обе функции HTML5).

Вы также можете отправлять файлы без 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 ( no 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 - это просто небольшая оболочка, которая добавляет запасную поддержку в метод jjuery ajax, но многие из вышеупомянутых плагинов, такие как плагин jQuery Form или jQuery File Upload включите весь стек от HTML5 до различных запасных вариантов и некоторые полезные функции для облегчения процесса. В зависимости от ваших потребностей и требований, вы можете рассмотреть пустую реализацию или один из этих плагинов.

83 голосов
/ 29 июля 2011

Этот 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');
  }
});
59 голосов
/ 25 января 2013

Я использовал приведенный ниже скрипт для загрузки изображений, который работает нормально.

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);
});

Объяснение

Я использую ответ div, чтобы показать анимацию загрузки и ответ после загрузки.

Самое приятное, что при использовании этого сценария вы можете отправлять дополнительные данные, такие как идентификаторы и т. Д., Вместе с файлом Я упомянул это extra-data как в сценарии.

На уровне PHP это будет работать как обычная загрузка файлов. дополнительные данные можно получить как $_POST данные.

Здесь вы не используете плагин и прочее. Вы можете изменить код, как вы хотите. Вы не слепо кодируете здесь. Это основная функциональность любой загрузки файла jQuery. Собственно Javascript.

44 голосов
/ 17 февраля 2013

Вы можете сделать это в обычном 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);
44 голосов
/ 08 августа 2014

Вы можете загрузить просто с помощью 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) {
                // ...
            }
       });
    }));
});
40 голосов
/ 26 июня 2014

Самый простой и надежный способ, которым я занимался в прошлом, - это просто нацелить скрытый тег 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, iFrames и onLoad

-note- вам нужно только продолжать чтение, если вы заинтересованы в том, как настроить блокировщик пользовательского интерфейса при загрузке / загрузке

В настоящее время Chrome не вызывает событие onLoad для iframe, когда оно используется для передачи файлов. Firefox, IE и Edge запускают событие onload для передачи файлов.

Единственным решением, которое я нашел для Chrome, было использование файла cookie.

Чтобы сделать это в основном, когда начата загрузка / выгрузка:

  • [Клиентская сторона] Начать интервал поиска файлов cookie
  • [Server Side] Делайте все, что вам нужно с данными файла
  • [Server Side] Установить cookie для клиентского интервала
  • [Client Side] Интервал видит cookie и использует его как событие onLoad. Например, вы можете запустить блокировщик пользовательского интерфейса, а затем onLoad (или при создании файла 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');
 });

А на стороне сервера, непосредственно перед передачей данных файла, создайте куки

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

Плагин увидит куки, а затем вызовет onComplete обратный вызов.

...