Я пытаюсь загрузить файл в бэкэнд узла, который использует Multer. Мултер требует, чтобы форма была представлена определенным образом. Если он не отправлен таким образом, параметр request.file
не будет undefined
. Я создал подход, который работает с использованием грубой силы. Этот рабочий подход выглядит так:
Индекс-1.html:
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<input type="file" id="selectedFile" name="selectedFile" /><br/><br/>
<input type="submit" value="Submit" id="btnSubmit"/>
</form>
...
var btn = document.getElementById('btnSubmit');
btn.addEventListener('click', function(e) {
e.preventDefault();
var form = $('#fileUploadForm')[0];
var data = new FormData(form);
console.log(data);
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/upload",
data: data,
processData: false,
contentType: false,
cache: false,
timeout: 600000,
success: function (data) {
console.log("SUCCESS!");
},
error: function (e) {
console.log("ERROR : ", e);
}
});
});
Код выше успешно отправляет файл на мой сервер. Этот сервер имеет следующее:
server.js
app.post('/upload', upload.single('selectedFile'), function(req, res) {
if (req.file) {
console.log('file uploaded');
} else {
console.log('no file');
}
res.send({});
});
Используя приведенный выше код, "файл загружен" отображается в окне консоли, как ожидалось. Однако мне нужен более динамичный подход. По этой причине мне нужно программно построить форму в JavaScript. В попытке сделать это я создал следующее:
index-2.html [мой пользовательский интерфейс]
var btn = document.getElementById('btnSubmit');
btn.addEventListener('click', function(e) {
var form = document.createElement('form');
form.action = '/upload';
form.method = 'POST';
form.enctype = 'multipart/form-data';
var node = document.createElement("input");
node.name = 'selectedFile';
node.value = GLOBAL_SELECTED_FILE;
form.appendChild(node);
var data = new FormData(form);
data.append('id', this.id);
console.log(data);
$.ajax({
type: 'POST',
url: '/profile-picture/upload',
enctype: 'multipart/form-data',
data: data,
contentType: false,
processData: false,
success: function(res) {
console.log('success!');
},
error: function(xhr, status, err) {
console.log('error');
}
});
});
Этот второй подход не работает. Чтобы уточнить, переменная GLOBAL_SELECTED_FILE
- это данные файла, выбранного из элемента ввода. Данные загружаются через FileReader api. Это выглядит так:
var GLOBAL_SELECTED_FILE = null;
var fileReader = new FileReader();
fileReader.onload = function(e) {
GLOBAL_SELECTED_FILE = e.target.result;
}
fileReader.readAsDataURL(fileSelected); // file selected comes from the onchange event on a <input type="file">..</input> element
В основном, я загружаю предварительный просмотр изображения. В любом случае, когда я нажимаю кнопку отправки в рабочей версии (index-1.html), я замечаю в Fiddler, что другое значение передается по значению, отправленному в index-2.html.
С помощью подхода в index-1.html Fiddler показывает что-то подобное на вкладке "TextView":
------WebKitFormBoundary183mBxXxf1HoE4Et
Content-Disposition: form-data; name="selectedFile"; filename="picture.PNG"
Content-Type: image/png
PNG
Однако, когда я смотрю на вкладке "TextView" в Fiddler для данных, отправленных через index-2.html, я вижу следующее:
------WebKitFormBoundary9UHBP02of1OI5Zb6
Content-Disposition: form-data; name="selectedFile"
data:image/png;base64,iVBORw0KGg[A LOT MORE TO GO]
Он, как и FormData, использует два разных кодировки для одного и того же значения. Но я не понимаю, почему. Как получить индекс-2.html для отправки изображения в том же формате, что и index-1.html, чтобы Multer заполнил свойство req.file?
Спасибо!
В index-1.html вы используете ввод файла:
<input type="file" id="selectedFile" name="selectedFile" />
В index-2.html вы создаете обычный ввод формы (а не ввод файла):
var node = document.createElement("input");
node.name = 'selectedFile';
node.value = GLOBAL_SELECTED_FILE;
form.appendChild(node);
Чтобы создать ввод файла, вам нужно будет добавить node.type = 'file'
. Однако вы не сможете установить значение, потому что ограничения безопасности браузера не позволяют установить значение входных файлов.
Вместо этого вам нужно добавить файл, который был выбран пользователем для объекта FormData
:
var data = new FormData(form);
data.append('id', this.id);
data.append('selectedFile', $('#fileInputElement')[0].files[0]);