Использование загрузки файлов HTML5 с AJAX и jQuery

72

По общему признанию, в Qaru есть похожие вопросы, но, похоже, они не соответствуют моим требованиям.

Вот что я хочу сделать:

  • Загрузите всю форму данных, одна часть которой представляет собой одиночный файл
  • Работа с библиотекой загрузки файлов Codeigniter

До сих пор все хорошо. Данные поступают в мою базу данных по мере необходимости. Но я также хотел бы представить свою форму через сообщение AJAX:

  • Использование собственного API-интерфейса HTML5, а не flash или iframe-решение
  • Предпочтительно взаимодействие с низкоуровневым методом .ajax() jQuery

Я думаю, что могу представить, как это сделать, автоматически загружая файл, когда значение поля изменяется с использованием чистого javascript, но я предпочел бы сделать все это одним махом для отправки в jQuery. Я думаю, что это невозможно сделать с помощью строк запроса, поскольку мне нужно передать весь файл, но я немного потерял, что делать в этот момент.

Можно ли это достичь?

Теги:
file-upload

2 ответа

89
Лучший ответ

Это не слишком сложно. Во-первых, посмотрите Интерфейс FileReader.

Итак, когда форма отправлена, поймите процесс отправки и

var file = document.getElementById('fileBox').files[0]; //Files[0] = 1st file
var reader = new FileReader();
reader.readAsText(file, 'UTF-8');
reader.onload = shipOff;
//reader.onloadstart = ...
//reader.onprogress = ... <-- Allows you to update a progress bar.
//reader.onabort = ...
//reader.onerror = ...
//reader.onloadend = ...


function shipOff(event) {
    var result = event.target.result;
    var fileName = document.getElementById('fileBox').files[0].name; //Should be 'picture.jpg'
    $.post('/myscript.php', { data: result, name: fileName }, continueSubmission);
}

Затем на стороне сервера (то есть myscript.php):

$data = $_POST['data'];
$fileName = $_POST['name'];
$serverFile = time().$fileName;
$fp = fopen('/uploads/'.$serverFile,'w'); //Prepends timestamp to prevent overwriting
fwrite($fp, $data);
fclose($fp);
$returnData = array( "serverFile" => $serverFile );
echo json_encode($returnData);

Или что-то в этом роде. Возможно, я ошибаюсь (и если да, то, пожалуйста, исправьте меня), но это должно хранить файл как нечто вроде 1287916771myPicture.jpg в /uploads/ на вашем сервере и отвечать переменной JSON (на функцию continueSubmission()), содержащий имя файла на сервере.

Отъезд fwrite() и jQuery.post().

На приведенной выше странице подробно описано, как использовать readAsBinaryString(), readAsDataUrl() и readAsArrayBuffer() для ваших других потребностей (например, изображения, видео и т.д.).

  • 0
    Эй, Кларк, я правильно понимаю? Это отправит загруженный файл, как только он будет загружен в конструктор FileReader из файловой системы, минуя низкоуровневый обработчик .ajax jQuery. Тогда остальные формы подадут как обычно?
  • 0
    Хорошо, поэтому я ошибался в своем понимании раньше. Теперь я беру readAsDataUrl изображения, добавляю его в строку данных в .ajax и отправляю всю мою информацию вместе. Мое предыдущее решение было связано с классом ввода файлов CodeIgniter по умолчанию, который собирал данные из $ _FILES ['field'], поэтому мне кажется, что мне нужно переключиться на другое решение для анализа данных изображения base64. Любые советы по этому поводу приветствуются, здесь вы можете перефразировать ваш ответ, и как только я закончу реализацию, я отмечу его как правильный.
Показать ещё 7 комментариев
6

С помощью jQuery (и без API FormData) вы можете использовать что-то вроде этого:

function readFile(file){
   var loader = new FileReader();
   var def = $.Deferred(), promise = def.promise();

   //--- provide classic deferred interface
   loader.onload = function (e) { def.resolve(e.target.result); };
   loader.onprogress = loader.onloadstart = function (e) { def.notify(e); };
   loader.onerror = loader.onabort = function (e) { def.reject(e); };
   promise.abort = function () { return loader.abort.apply(loader, arguments); };

   loader.readAsBinaryString(file);

   return promise;
}

function upload(url, data){
    var def = $.Deferred(), promise = def.promise();
    var mul = buildMultipart(data);
    var req = $.ajax({
        url: url,
        data: mul.data,
        processData: false,
        type: "post",
        async: true,
        contentType: "multipart/form-data; boundary="+mul.bound,
        xhr: function() {
            var xhr = jQuery.ajaxSettings.xhr();
            if (xhr.upload) {

                xhr.upload.addEventListener('progress', function(event) {
                    var percent = 0;
                    var position = event.loaded || event.position; /*event.position is deprecated*/
                    var total = event.total;
                    if (event.lengthComputable) {
                        percent = Math.ceil(position / total * 100);
                        def.notify(percent);
                    }                    
                }, false);
            }
            return xhr;
        }
    });
    req.done(function(){ def.resolve.apply(def, arguments); })
       .fail(function(){ def.reject.apply(def, arguments); });

    promise.abort = function(){ return req.abort.apply(req, arguments); }

    return promise;
}

var buildMultipart = function(data){
    var key, crunks = [], bound = false;
    while (!bound) {
        bound = $.md5 ? $.md5(new Date().valueOf()) : (new Date().valueOf());
        for (key in data) if (~data[key].indexOf(bound)) { bound = false; continue; }
    }

    for (var key = 0, l = data.length; key < l; key++){
        if (typeof(data[key].value) !== "string") {
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"; filename=\""+data[key].value[1]+"\"\r\n"+
                "Content-Type: application/octet-stream\r\n"+
                "Content-Transfer-Encoding: binary\r\n\r\n"+
                data[key].value[0]);
        }else{
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"\r\n\r\n"+
                data[key].value);
        }
    }

    return {
        bound: bound,
        data: crunks.join("\r\n")+"\r\n--"+bound+"--"
    };
};

//----------
//---------- On submit form:
var form = $("form");
var $file = form.find("#file");
readFile($file[0].files[0]).done(function(fileData){
   var formData = form.find(":input:not('#file')").serializeArray();
   formData.file = [fileData, $file[0].files[0].name];
   upload(form.attr("action"), formData).done(function(){ alert("successfully uploaded!"); });
});

С API FormData вам просто нужно добавить все поля формы в объект FormData и отправить его через $.ajax({url: url, data: formData, processData: false, contentType: false, type: "POST" } )

  • 0
    Это решение не устраняет ограничение, накладываемое XMLHttpRequest.send () на данные, передаваемые через него. Когда передается строка (например, ваш multipart), send () не поддерживает двоичные данные. Ваша составная часть здесь будет обрабатываться как строка utf-8 и будет блокировать или повреждать двоичные данные, которые не являются допустимыми utf-8. Если вам действительно нужно избегать FormData, вам нужно использовать XMLHttpRequest.sendAsBinary () ( доступное многозаполнение . К сожалению, это означает, что использование jQuery для вызова ajax становится намного сложнее.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню