Використання файлів HTML5 для завантаження за допомогою AJAX та jQuery


84

Слід визнати, що на Stack Overflow є подібні запитання, але, здається, жоден не відповідає моїм вимогам.

Ось що я хочу зробити:

  • Завантажте цілу форму даних, одна частина яких - це один файл
  • Робота з бібліотекою для завантаження файлів Codeigniter

До цього часу все добре. Дані потрапляють до моєї бази даних у міру необхідності. Але я також хотів би надіслати свою форму через допис AJAX:

  • Використовуючи власний API файлу HTML5, а не Flash або рішення iframe
  • Переважно взаємодія з низькорівневим .ajax()методом jQuery

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

Чи можна цього досягти?


Я не маю уявлення про частину Codeigniter, але для частини jQuery знайдіть цей плагін .
BalusC

Відповіді:


93

Це не надто складно. По-перше, погляньте на інтерфейс 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()для інших ваших потреб (наприклад, зображення, відео тощо).


Ей Кларк, я правильно розумію? Це надішле завантажений файл, як тільки він буде завантажений у конструктор FileReader із файлової системи, минаючи низькорівневий обробник .ajax jQuery. Тоді решта форми подаватиметься як зазвичай?
Джошуа Коді

Гаразд, тому я раніше помилявся у своєму розумінні. Тепер я беру readAsDataUrl зображення, додаю його до мого рядка даних у .ajax і подаю всю свою інформацію разом. Моє попереднє рішення стосувалося класу введення файлів CodeIgniter за замовчуванням, який захоплював дані з $ _FILES ['поле'], тому, схоже, мені потрібно буде перейти на інше рішення для синтаксичного аналізу даних зображення base64. Будь-яка порада щодо цього вітається, проголосуючи тут вашу відповідь, і як тільки я закінчу реалізацію, я позначу її як правильну.
Джошуа Коді

1
@ Джошуа Коді - я оновив відповідь, щоб дати трохи більше деталей. Вам доведеться пробачити, що я не використовував CodeIgniter багато місяців і не міг сказати вам, як інтегрувати це в їх базу коду. Я не впевнений, чому вам потрібно завантажувати файл перед подачею, але це має принаймні дати вам підказку. (Ви також можете вставити зображення в базу даних, якщо це для вас краще).
clarkf

@Clarkf, мені не потрібно завантажувати файли перед поданням, я неправильно зрозумів ваш попередній приклад :) Після того, як SO пішов і я провів деякий час на w3 та HTML5Rocks , я почав розуміти. Я спробую це і повернусь сюди.
Джошуа Коді,

Гаразд, возився з цим цілий ранок. Здається, PHP повертає погано відформатовані файли. Перегляньте два зображення , одне відразу відтворене, а друге після $ _POST на сервер і негайне відлуння. Розбіжність двох елементів свідчить про те , що, мабуть, PHP зачищає всі символи "+". Str_replace виправляє це для негайного повернення, але збережений файл все ще пошкоджений і не може бути відкритий через мою файлову систему. Крім того, продовжуючи і позначаючи це як правильне. Велике спасибі за вашу допомогу.
Джошуа Коді,

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


1
Це рішення не стосується обмежень, які XMLHttpRequest.send () накладає на дані, що проходять через нього. Коли передається рядок (наприклад, ваша багаточастинна), send () не підтримує двійкові дані. Ваша багаточастинка тут буде трактуватися як рядок utf-8, і вона буде подавлятися або пошкоджувати двійкові дані, які не є дійсними utf-8. Якщо вам дійсно потрібно уникати FormData, вам потрібно використовувати XMLHttpRequest.sendAsBinary () ( доступне полізаповнення . На жаль, це означає, що використання jQuery для виклику ajax стає набагато складнішим.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.