Внешний интерфейс не может напрямую управлять локальными файлами, как родное приложение. В противном случае открытие веб-страницы может привести к краже файлов на компьютере пользователя, поэтому он должен запускаться пользователем. Пользователь может работать и запускать следующими тремя способами. :
- Выберите локальный файл по типу ввода = "файл"
- Перетащите файлы
- Скопируйте и вставьте в поле редактирования
Первый — самый распространенный метод, обычно это настройка кнопки, а затем ее закрытие, потому что ввод type="file" не так просто изменить стиль. Напишите элемент управления выбором следующим образом и поместите его в форму:
<form>
<input type="file" id="file-input" name="fileContent">
</form>
тогда вы можете использоватьFormDataПолучить содержимое всей формы:
$("#file-input").on("change", function() {
console.log(`file name is ${this.value}`);
let formData = new FormData(this.form);
formData.append("fileName", this.value);
console.log(formData);
});
Входное значение и formData печатаются следующим образом:
Видно, что путь к файлу является поддельным путем, то есть реальное место хранения файла не может быть получено в браузере. При этом FormData печатает пустой Object, но это не значит, что его содержимое пусто, но оно прозрачно для фронтенд-разработчиков, и не может просматривать, модифицировать или удалять содержимое внутри, а только дописывать для добавления полей .
FormData не может получить содержимое файла и использоватьFileReaderСодержимое всего файла может быть прочитано. После того, как пользователь выберет файл, input.files может получить файл, выбранный пользователем, следующим образом:
$("#file-input").on("change", function() {
let fileReader = new FileReader(),
fileType = this.files[0].type;
fileReader.onload = function() {
if (/^image/.test(fileType)) {
// 读取结果在fileReader.result里面
$(`<img src="${this.result}">`).appendTo("body");
}
}
// 打印原始File对象
console.log(this.files[0]);
// base64方式读取
fileReader.readAsDataURL(this.files[0]);
});
Распечатка исходного объекта File выглядит следующим образом:
Это экземпляр window.File, который содержит время модификации файла, имя файла, размер файла, MIME-тип файла и т. д. Если вам нужно ограничить размер загружаемого файла, вы можете определить, превышен ли атрибут размера, единица измерения — байты, а чтобы определить, является ли это файлом изображения, вы можете передать тип типа, начинается ли он с изображения. Судя по суффиксу имени файла, может быть неточным, и это суждение будет более точным. В приведенном выше коде используется обычное суждение.Если это изображение, назначьте его src для img и добавьте в dom, но с этим кодом есть проблема, то есть не все изображения в сети могут отображаться через тег img Обычно это jpg/png/gif, поэтому вам нужно снова оценить формат изображения.Например, вы можете изменить решение на:
/^image\/[jpeg|png|gif]/.test(this.type)
Затем создайте экземпляр FileReader, вызовите его readAsDataURL и передайте ему объект File, прослушайте его событие onload, и результат, прочитанный после загрузки, будет в его свойстве result. Он имеет формат base64 и может быть напрямую назначен src изображения.
Помимо чтения в формате base64, FileReader также может читать следующие форматы:
// 按base64的方式读取,结果是base64,任何文件都可转成base64的形式
fileReader.readAsDataURL(this.files[0]);
// 以二进制字符串方式读取,结果是二进制内容的utf-8形式,已被废弃了
fileReader.readAsBinaryString(this.files[0]);
// 以原始二进制方式读取,读取结果可直接转成整数数组
fileReader.readAsArrayBuffer(this.files[0]);
Остальные в основном читаются как ArrayBuffer, что является результатом в необработанном двоичном формате. ArrayBuffer печатается следующим образом:
Видно, что он также прозрачен для фронтенд-разработчиков, и не может напрямую читать содержимое внутри, но длину можно получить через ArrayBuffer.length, а также его можно преобразовать в целочисленный массив, так что исходный бинарник содержимое файла можно узнать:
let buffer = this.result;
// 依次每字节8位读取,放到一个整数数组
let view = new Uint8Array(buffer);
console.log(view);
Если через вторуюперетащить, Как я должен прочитать файл? Следующий html (стиль опущен):
<div class="img-container">
drop your image here
</div>
Это отобразит окно на странице:
Затем прослушайте его событие перетаскивания:
$(".img-container").on("dragover", function (event) {
event.preventDefault();
})
.on("drop", function(event) {
event.preventDefault();
// 数据在event的dataTransfer对象里
let file = event.originalEvent.dataTransfer.files[0];
// 然后就可以使用FileReader进行操作
fileReader.readAsDataURL(file);
// 或者是添加到一个FormData
let formData = new FormData();
formData.append("fileContent", file);
})
Данные находятся в event.dataTransfer.files события drop.После получения объекта File можно выполнить ту же операцию, что и поле ввода, то есть использовать FileReader для его чтения, либо создать новый пустой formData, а затем добавить его в formData. .
третийспособ склеивания, обычно в поле редактирования, например, установив для contenteditable div значение true:
<div contenteditable="true">
hello, paste your image here
</div>
Вставленные данные находятся в файле event.clipboardData.files:
$("#editor").on("paste", function(event) {
let file = event.originalEvent.clipboardData.files[0];
});
Но вставка Safari не проходит через событие, она напрямую добавляет картинку в поле ввода, как показано на следующем рисунке:
Он создает новый тег img и указывает src img на локальные данные большого двоичного объекта. Что такое блоб и как прочитать содержимое блоба?
Большой двоичный объект — это файловый формат хранения, который может хранить контент практически в любом формате, например json:
let data = {hello: "world"};
let blob = new Blob([JSON.stringify(data)],
{type : 'application/json'});
Чтобы получить локальные данные большого двоичного объекта, мы можем использовать ajax для выполнения локального запроса:
$("#editor").on("paste", function(event) {
// 需要setTimeout 0等图片出来了再处理
setTimeout(() => {
let img = $(this).find("img[src^='blob']")[0];
console.log(img.src);
// 用一个xhr获取blob数据
let xhr = new XMLHttpRequest();
xhr.open("GET", img.src);
// 改变mime类型
xhr.responseType = "blob";
xhr.onload = function () {
// response就是一个Blob对象
console.log(this.response);
};
xhr.send();
}, 0);
});
Приведенный выше код печатает блоб следующим образом:
Он может получить свой размер и тип, но конкретное содержимое также невидимо.У него есть метод среза, который можно использовать для вырезания больших файлов. Как и File, его содержимое можно прочитать с помощью FileReader:
function readBlob(blobImg) {
let fileReader = new FileReader();
fileReader.onload = function() {
console.log(this.result);
}
fileReader.onerror = function(err) {
console.log(err);
}
fileReader.readAsDataURL(blobImg);
}
readBlob(this.response);
Кроме того, вы также можете использовать window.URL для чтения, это новый API, часто используемый вместе с Service Worker, потому что URL-адрес часто анализируется в SW. Следующий код:
function readBlob(blobImg) {
let urlCreator = window.URL || window.webkitURL;
// 得到base64结果
let imageUrl = urlCreator.createObjectURL(this.response);
return imageUrl;
}
readBlob(this.response);
Что касается src, используется ссылка blob.Помимо упомянутого выше img, еще одним очень распространенным является тег видео.Например, видео youtobe использует blob:
Этот тип данных не является локальным напрямую, а постоянно запрашивается видеоданные, а затем добавляется к видео через носитель контейнера больших двоичных объектов, который также создается с помощью API URL:
let mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
let sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
sourceBuffer.appendBuffer(buf);
Я не практиковал это подробно, поэтому я не буду обсуждать это дальше.
Выше мы использовали три способа получить содержимое файла и в итоге получили:
- Формат FormData
- Двоичный формат base64 или ArrayBuffer, читаемый FileReader
Если это непосредственно FormData, то его можно отправить напрямую с помощью ajax без какой-либо обработки:
let form = document.querySelector("form"),
formData = new FormData(form),
formData.append("fileName", "photo.png");
let xhr = new XMLHttpRequest();
// 假设上传文件的接口叫upload
xhr.open("POST", "/upload");
xhr.send(formData);
При использовании jQuery установите для двух свойств значение false:
$.ajax({
url: "/upload",
type: "POST",
data: formData,
processData: false, // 不处理数据
contentType: false // 不设置内容类型
});
Поскольку jQuery автоматически выполняет экранирование содержимого и автоматически устанавливает тип mime запроса в соответствии с данными, здесь скажите jQuery отправить его напрямую с помощью xhr.send.
Наблюдайте за данными, запрошенными консолью:
Видно, что это другой способ соединения параметров с &. Его формат кодирования — multipart/form-data, который является enctype, записанным в форме формы выгружаемого файла:
<form enctype="multipart/form-data" method="post">
<input type="file" name="fileContent">
</form>
Если xhr.send имеет тип FormData, он автоматически установит enctype.Если вы используете форму по умолчанию для отправки загружаемого файла, вы должны установить этот атрибут в форме, поскольку загружаемый файл может использовать только эту кодировку POST. Обычно используемая кодировка POST — это application/x-www-form-urlencoded, что совпадает с GET.В отправляемых данных параметры и параметры соединяются с помощью &, например:
key1=value1&key2=value2
Специальные символы экранированы. Эти данные POST помещаются в тело запроса, а GET пишется в URL-адресе. Если используется jq, jq поможет вам написать и экранировать.
Для multipart/form-data, используемого для загрузки файлов, параметры и параметры разделяются одной и той же строкой.
------WebKitFormBoundary72yvM25iSPYZ4a3F
Этот символ обычно длинный и случайный, потому что необходимо убедиться, что эта строка не появляется в обычном содержимом, чтобы специальные символы содержимого не нужно было экранировать.
ContentType запроса устанавливается браузером на:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundary72yvM25iSPYZ4a3F
Бэкенд-сервис знает, как парсить такой кусок данных через this. (Обычно это обрабатывается используемой структурой, и конкретному интерфейсу не нужно заботиться о том, как его следует анализировать)
Если результатом чтения является ArrayBuffer, его также можно отправить напрямую с помощью xhr.send, но обычно мы не будем отправлять содержимое файла напрямую, а будем использовать определенное имя поля, равное содержимому файла. Если вы читаете его как ArrayBuffer, а затем загружаете, эффект не очень большой.Лучше добавлять содержимое объекта File непосредственно с помощью formData, потому что вышеперечисленные три метода могут получить объект File. Если вначале это ArrayBuffer, его можно преобразовать в большой двоичный объект, а затем добавить к FormData.
Наиболее часто используемым должен быть base64, потому что интерфейсу часто нужно обрабатывать картинку.После чтения в формате base64 его можно нарисовать на холсте, а затем выполнить некоторую обработку, такую как сжатие, обрезка, поворот и т. д. . Наконец, используйте холст для экспорта изображения в формате base64.Как загрузить формат base64?
Первый заключается в написании формата multipart/form-data, загруженного формой, а затем его отправке с помощью xhr.sendAsBinary следующим образом:
let base64Data = base64Data.replace(/^data:image\/[^;]+;base64,/, "");
let boundary = "----------boundaryasoifvlkasldvavoadv";
xhr.sendAsBinary([
// name=data
boundary,
'Content-Disposition: form-data; name="data"; filename="' + fileName + '"',
'Content-Type: ' + "image/" + fileType, '',
atob(base64Data), boundary,
//name=imageType
boundary,
'Content-Disposition: form-data; name="imageType"', '',
fileType,
boundary + '--'
].join('\r\n'));
В приведенном выше коде используется API-интерфейс window.atob, который может восстановить base64 до строкового представления исходного содержимого, как показано на следующем рисунке:
btoa преобразует содержимое в кодировку base64, а atob восстанавливает base64. Перед вызовом atob необходимо удалить строку, представляющую формат контента, не относящийся к контенту base64, то есть замену обработки в первой строке приведенного выше кода.
Это похоже на использование formData, но поскольку sendAsBinary устарел, не рекомендуется использовать этот метод в новом коде. тогда что нам делать?
Вы можете преобразовать base64 в blob, а затем добавить его в formData, следующая функция (изb64-to-blob) Может передавать Base64 в BLOB:
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
Затем вы можете добавить к formData:
let blob = b64toBlob(b64Data, "image/png"),
formData = new FormData();
formData.append("fileContent", blob);
Таким образом, вам не нужно самостоятельно писать данные формата multipart/form-data.
Приведенный выше API для обработки и загрузки файлов может быть совместим с IE10+, если вы хотитеСовместимость со старыми браузерамиКак я должен это делать?
Могус iframe, принцип заключается в том, что отправка формы по умолчанию обновит страницу или перейдет к URL-адресу, указанному целью, но если цель irmae указывает на iframe, тогда iframe будет обновлен, и возвращенный результат также будет отображаться в ifame, а затем получить ifrmae Контент может получить результат, возвращаемый интерфейсом загрузки.
Следующий код:
let iframe = document.createElement("iframe");
iframe.display = "none";
iframe.name = "form-iframe";
document.body.appendChild(iframe);
// 改变form的target
form.target = "form-iframe";
iframe.onload = function() {
//获取iframe的内容,即服务返回的数据
let responseText = this.contentDocument.body.textContent
|| this.contentWindow.document.body.textContent;
};
form.submit();
form.submit инициирует отправку формы. Когда запрос будет завершен (успешно или неудачно), будет запущено событие onload iframe, а затем возвращенные данные будут получены в событии onload. Если запрос не выполнен, содержимое в iframe будет пусто, вы можете использовать этот запрос на суждение успешно.
Невозможно получить его с помощью iframeЗагрузить прогресс, используйте xhr, чтобы получить ход текущей загрузки, которая появилась в XMLHttpRequest 2.0:
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
// 当前上传进度的百分比
duringCallback ((event.loaded / event.total)*100);
}
};
Это сделает реальный индикатор загрузки.
В этой статье обсуждаются три интерактивных метода чтения.Вы можете получить объект файла файла в input.files с помощью элемента управления вводом, перетащить его в event.dataTransfer.files события перетаскивания и вставить событие вставки в событие. .files, Safari, урод, вставляет в редакторе src, указывающий на локальный тег img.Он может загрузить локальные данные блоба, отправив запрос, а затем прочитать их через FileReader, или напрямую добавить их в formData. Полученный объект File можно напрямую добавить в FormData.Если вам нужно прочитать формат base64 для обработки, вы можете преобразовать обработанный base64 в данные BLOB-объектов и добавить их в formData. В старых браузерах вы можете использовать iframe для решения проблемы обновления страницы или перехода по ней после отправки формы.
Короче говоря, интерфейсная обработка и загрузка локальных файлов должны быть почти одинаковыми, но должно быть много деталей, которые не были упомянуты, читатели могут попрактиковаться самостоятельно, следуя указаниям, перечисленным в этой статье. Пожалуйста, дайте мне знать, если есть другие способы загрузки.