В этой статье Brother Abao расскажет, как внешний интерфейс выполняет обработку изображений в соответствии со следующим процессом, а затем перемежается с точками знаний, связанными с двоичным файлом, Blob, URL-адресом BLOB-объекта, Base64, URL-адресом данных, ArrayBuffer, TypedArray, DataView и изображением. сжатие.
Прочитав эту статью, друзья смогут легко разобраться в следующих схемах преобразования:
О чем вы все еще сомневаетесь? Следуя по стопам брата Абао, давайте вместе поиграем с внешним бинарным кодом. Друзья, простите брата Абао за «нарциссизм» В следующих примерах мы будем использовать личный аватар брата Абао в качестве демонстрационного материала.
Прочитайте последние популярные статьи брата А Бао (спасибо за вашу поддержку и поддержку 🌹🌹🌹):
- 1.2W Words | Удивительное вводное руководство по TypeScript
- 10 крутых эффектов при наведении изображения члена(686+ 👍)
- 10 лучших проектов TS, которые ослепляют ваши глаза(679+ 👍)
- Одна статья для понимания дженериков и приложений TypeScript (7,8 тыс. слов)(549+ 👍)
- Не беспокойтесь об обработке изображений, я дам вам десять маленьких помощников.(454+ 👍)
Хорошо, теперь давайте перейдем к первой ссылке:"Выберите Локальное изображение -> Предварительный просмотр изображения".
1. Выберите Локальное изображение -> Предварительный просмотр изображения.
1.1 FileReader API
в поддержкуFileReader APIВ браузере мы также можем использовать этот API, чтобы легко реализовать функцию локального предварительного просмотра изображения.
(Источник изображения: https://caniuse.com/#search=filereader)
Как видно из приведенного выше рисунка, совместимость API хорошая, и мы можем с уверенностью ее использовать. Брат Абао не будет здесь вдаваться в подробности.FileReader API, давайте посмотрим непосредственно на то, как использовать его для локального предварительного просмотра изображения, конкретный код выглядит следующим образом:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片本地预览示例</title>
</head>
<body>
<h3>阿宝哥:图片本地预览示例</h3>
<input type="file" accept="image/*" onchange="loadFile(event)" />
<img id="previewContainer" />
<script>
const loadFile = function (event) {
const reader = new FileReader();
reader.onload = function () {
const output = document.querySelector("#previewContainer");
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
};
</script>
</body>
</html>
В приведенном выше примере мы имеемfile
Привязка типизированного поля вводаonchange
обработчик событияloadFile
, в этой функции мы создаем объект FileReader и привязываемся к этому объектуonload
соответствующий обработчик событий, а затем вызвать объект FileReaderreadAsDataURL()
метод для преобразования объекта File, соответствующего локальному изображению, в URL-адрес данных.
❝Фактически, для объекта FileReader, помимо поддержки преобразования объектов File/Blob в URL-адреса данных, он также предоставляет
❞readAsArrayBuffer()
а такжеreadAsText()
Методы преобразования объектов File/Blob в другие форматы данных.
Когда файл будет прочитан, он вызовет привязкуonload
Функция обработчика событий внутри функции обработчика получит данные URL-адреса данных, назначенныеimg
элементальsrc
атрибут, чтобы реализовать локальный предварительный просмотр изображения.
Используя инструменты разработчика Chrome, мы можемElements
На панели см. URL-адрес данных""Ароматный"":
В зеленой рамке в правой части рисунка мы ясно видимimg
элементsrc
Значение свойства представляет собой строку очень"странность"Нить:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhAAAAIwCAYAAADXrFK...
На самом деле эта странная строка называется Data URL и состоит из четырех частей: префикса (data:
), тип MIME, указывающий тип данных, необязательный, если нетекстовыйbase64
Тег, сами данные:
data:[<mediatype>][;base64],<data>
mediatype
является строкой типа MIME, такой как "image/png
" представляет файл изображения PNG. Если этот параметр опущен, по умолчаниюtext/plain;charset=US-ASCII
. Если данные имеют текстовый тип, вы можете внедрить текст напрямую (используя соответствующие символы сущности или escape-символы в зависимости от типа документа). Если это двоичные данные, вы можете закодировать данные в base64 перед встраиванием.
❝MIME (многоцелевые расширения почты Интернета) — это тип многоцелевого расширения почты Интернета, который представляет собой способ установить файл с определенным расширением для открытия приложением.При доступе к файлу расширения браузер будет автоматически использовать указанный расширение приложение для открытия. Он в основном используется для указания некоторых определяемых клиентом имен файлов, а также некоторых методов открытия медиафайлов.
Общие типы MIME: язык гипертекстовой разметки text .html text/html, изображение PNG .png image/png, обычный текст .txt text/plain и т. д.
❞
В процессе разработки веб-проекта, чтобы уменьшить количество HTTP-запросов и соответствовать некоторым меньшим значкам, мы обычно рассматриваем возможность использования формы URL-адреса данных для встраивания в файлы HTML или CSS."Однако следует отметить, что если изображение большое и цветовой уровень изображения насыщенный, использовать этот метод нецелесообразно, так как строка, закодированная base64 изображения, очень велика, что значительно увеличит размер HTML-страницы, тем самым влияя на размер HTML-страницы, скорость загрузки."
В URL-адресе данных данные являются очень важной частью, которая представлена строкой в кодировке base64. Итак, чтобы понять URL-адрес данных, мы также должны понимать Base64.
1.2 Base64
"Base64"представляет собой представление двоичных данных, основанное на 64 печатных символах. из-за"2⁶ = 64", поэтому каждые 6 бит — это единица, соответствующая определенному печатному символу. 3 байта имеют 24 бита, что соответствует 4 единицам base64, т. е. 3 байта могут быть представлены 4 печатными символами. Соответствующий процесс преобразования показан на следующем рисунке:
"Base64 часто используется для представления, передачи и хранения некоторых двоичных данных, включая электронные письма MIME и некоторые сложные данные в XML, при работе с текстовыми данными."В электронной почте в формате MIME base64 может использоваться для кодирования данных последовательности двоичных байтов в текст, состоящий из последовательности символов ASCII. При использовании укажите base64 в методе кодирования передачи. Используемые символы включают 26 прописных и строчных латинских букв, 10 цифр, знак плюс + и косую черту /, всего 64 символа, а знак равенства = используется в качестве суффикса.
Соответствующая индексная таблица Base64 выглядит следующим образом:
Поняв вышеизложенные знания, давайте кодMan
Например, чтобы интуитивно чувствовать процесс кодирования.Man
Он состоит из трех символов M, a и n и соответствующих им кодов ASCII: 77, 97 и 110.
Затем мы выполняем операцию кодирования base64 с каждыми 6 битами как единицей, как показано на следующем рисунке:
На рисунке показано,Man
(3 байта) закодированный результатTWFu
(4 байта), очевидно, объем увеличится на 1/3 после кодирования base64.Man
Длина этой строки ровно 3, что мы можем представить в 4 единицах base64. Но что, если длина кодируемой строки не является целым числом, кратным 3?
"Если количество кодируемых байтов не делится на 3, а в конце будут лишние 1 или 2 байта, то с этим можно справиться следующим способом: сначала использовать 0-байтовое значение, чтобы компенсировать конец, чтобы он делился на 3, затем выполните кодировку base64."
Взяв закодированный символ A в качестве примера, количество байтов, которые он занимает, равно 1, что не может делиться на 3 и должно быть дополнено 2 байтами, как показано на следующем рисунке:
Как видно из приведенного выше рисунка, результат символа A после кодирования base64 равенQQ==
, два следующих за результатом=
Представляет количество байтов, которые необходимо восполнить. И последний 1 блок байтов base64 имеет 4 бита со значением 0.
Давайте посмотрим на другой пример, предположим, что кодируемая строка имеет видBC
, число байтов, которое он занимает, равно 2, что не может делиться на 3 и должно быть дополнено 1 байтом, как показано на следующем рисунке:
Как видно из приведенного выше рисунка, результат кодирования base64 строки BC равенQkM=
, 1 после результата=
Представляет количество байтов, которые необходимо восполнить. И последний 1 блок байтов base64 имеет 2 бита со значением 0.
В JavaScript две функции используются для декодирования и кодирования строк base64 соответственно:
- btoa(): эта функция создает строку ASCII в кодировке base64 на основе «строки» двоичных данных.
- atob(): эта функция способна декодировать строковые данные в кодировке base64.
1.2.1 Пример ВТОА
const name = 'Semlinker';
const encodedName = btoa(name);
console.log(encodedName); // U2VtbGlua2Vy
1.2.2 Примеры использования atob
const encodedName = 'U2VtbGlua2Vy';
const name = atob(encodedName);
console.log(name); // Semlinker
Для двух методов atob и btoa a представляет ASCII, а b представляет Blob, который является двоичным. Таким образом, atob означает преобразование ASCII в двоичный код, что соответствует операции декодирования. А btoa представляет двоичный код в ASCII, что соответствует операции кодирования. Поняв значения a и b в методе, мы не будем использовать их неправильно в дальнейшей работе.
Я считаю, что увидев это, друзья уже имеют определенное представление о base64. Следует отметить, что base64 — это только метод кодирования данных, целью которого является обеспечение безопасной передачи данных. Но стандартная кодировка base64 может быть декодирована без дополнительной информации и полностью обратима. Поэтому, когда речь идет о передаче приватных данных, кодировку base64 нельзя использовать напрямую, а следует использовать специальный алгоритм симметричного или асимметричного шифрования.
2. Скачать картинки из сети -> превью картинки
Помимо получения изображений локально, мы также можем использоватьfetch APIПолучите изображения из Интернета, а затем просмотрите изображения. Конечно, для адреса изображения, к которому можно получить обычный доступ в сети, мы можем напрямую назначить адресimg
Элемент не должен проходитьfetch APIИдите по большому кругу. Если вам необходимо выполнить специальную обработку изображения при отображении изображения, например расшифровку данных изображения, вы можете рассмотретьWeb Workerиспользуется вfetch APIПолучите данные изображения и выполните операции расшифровки.
Для простоты мы не рассматриваем специальные сценарии. Сначала давайте посмотрим наfetch APIСовместимость:
(Источник изображения: https://caniuse.com/#search=fetch)
Затем мы используемfetch APIПолучите аватар Brother A Bao с Github, конкретный код выглядит следующим образом:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>获取远程图片预览示例</title>
</head>
<body>
<h3>阿宝哥:获取远程图片预览示例</h3>
<img id="previewContainer" style="width: 50%;"/>
<script>
const image = document.querySelector("#previewContainer");
fetch("https://avatars3.githubusercontent.com/u/4220799")
.then((response) => response.blob())
.then((blob) => {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
});
</script>
</body>
</html>
В приведенном выше примере мы используем fetch API для загрузки аватара Baoge из Github, когда запрос выполнен успешно, преобразуем объект ответа (Response) в объект Blob, а затем используемURL.createObjectURL
метод, создайте URL-адрес объекта и назначьте егоimg
элементальsrc
свойства для отображения изображения.
Используя инструменты разработчика Chrome, мы можемElements
В панели см. URL объекта""Ароматный"":
В зеленой рамке в правой части рисунка мы ясно видимimg
элементsrc
Значение свойства представляет собой строку очень"особый"Нить:
blob:null/ab24c171-1c5f-4de1-a44e-568bc1f77d7b
Специальная строка выше, мы называем ее"Object URL", что намного проще, чем URL-адрес данных, представленный ранее. Далее давайте познакомимся с протоколом Object URL.
2.1 Object URL
URL-адрес объекта — это псевдопротокол, также известный как URL-адрес BLOB-объекта. Он позволяет использовать объекты Blob или File в качестве источников URL-адресов для изображений, ссылок для загрузки двоичных данных и многого другого. В браузере мы используемURL.createObjectURL
метод создания URL-адреса BLOB-объекта, который принимаетBlob
объекта и создать для него уникальный URL видаblob:<origin>/<uuid>
, соответствующий пример выглядит следующим образом:
blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641
Внутри браузера для каждого проходаURL.createObjectURL
Сгенерированный URL хранит"URL-адрес → большой двоичный объект"карта. Поэтому такие URL короче, но к ним можно получить доступBlob
. Сгенерированный URL-адрес действителен только в том случае, если текущий документ открыт. Но если вы посетите URL-адрес большого двоичного объекта, который больше не существует, вы получите ошибку 404 в своем браузере.
Приведенный выше URL-адрес большого двоичного объекта выглядит великолепно, но на самом деле он имеет побочные эффекты. Хотя сопоставление URL-адрес → BLOB-объект сохраняется, сам BLOB-объект все еще находится в памяти, и браузер не может его освободить. Сопоставление автоматически очищается, когда документ выгружается, поэтому объект Blob затем освобождается. Однако, если приложение является долгоживущим, это не произойдет в ближайшее время. Таким образом, если мы создадим URL-адрес большого двоичного объекта, он будет существовать в памяти, даже если этот большой двоичный объект больше не нужен.
Для этой проблемы мы можем вызватьURL.revokeObjectURL(url)
удаляет ссылку из внутренней карты, позволяя удалить большой двоичный объект, если нет других ссылок, и освобождает память.
Поскольку мы говорили об URL-адресах BLOB-объектов, мы должны упомянуть Blob. Так что же такое блобы? Давайте продолжим чтение.
2.2 Blob API
Blob (Binary Large Object) представляет собой большой объект двоичного типа. В системе управления базами данных двоичные данные хранятся как набор одного объекта. Блобы обычно представляют собой изображения, звуковые или мультимедийные файлы."Объекты типа Blob в JavaScript представляют собой неизменяемые файловоподобные примитивные данные."Чтобы более интуитивно чувствовать объект Blob, давайте сначала воспользуемся конструктором Blob для создания объекта myBlob, как показано на следующем рисунке:
Как видите, объект myBlob имеет два свойства: размер и тип. вsize
атрибут используется для представления размера данных в байтах,type
является строкой типа MIME. Большие двоичные объекты не обязательно представляют данные в собственном формате JavaScript. НапримерFile
интерфейс на основеBlob
, который наследует функциональные возможности BLOB-объектов и расширяет их для поддержки файлов в системе пользователя.
Blob
опциональной строкойtype
(обычно тип MIME) иblobParts
сочинение:
❝MIME (многоцелевые расширения почты Интернета) — это тип многоцелевого расширения почты Интернета, который представляет собой способ установить файл с определенным расширением для открытия приложением.При доступе к файлу расширения браузер будет автоматически использовать указанный расширение приложение для открытия. Он в основном используется для указания некоторых определяемых клиентом имен файлов, а также некоторых методов открытия медиафайлов.
Общие типы MIME: язык гипертекстовой разметки text .html text/html, изображение PNG .png image/png, обычный текст .txt text/plain и т. д.
❞
2.2.1 Конструктор больших двоичных объектов
Синтаксис конструктора Blob:
var aBlob = new Blob(blobParts, options);
Соответствующие параметры описываются следующим образом:
- blobParts: это массив объектов, таких как ArrayBuffer, ArrayBufferView, Blob, DOMString и т. д. DOMStrings будут закодированы как UTF-8.
- options: необязательный объект, содержащий следующие два свойства:
- тип - значение по умолчанию
""
, который представляет тип MIME содержимого массива, которое будет помещено в большой двоичный объект. - окончания - значение по умолчанию
"transparent"
, используется для указания окончания строки, включающей\n
как пишется строка. Это одно из следующих двух значений:"native"
, что означает, что символ конца строки заменяется символом новой строки, подходящим для файловой системы основной операционной системы, или"transparent"
, что означает, что терминатор, сохраненный в большом двоичном объекте, останется неизменным.
- тип - значение по умолчанию
"Пример 1. Создание большого двоичного объекта из строки"
let myBlobParts = ['<html><h2>Hello Semlinker</h2></html>']; // an array consisting of a single DOMString
let myBlob = new Blob(myBlobParts, {type : 'text/html', endings: "transparent"}); // the blob
console.log(myBlob.size + " bytes size");
// Output: 37 bytes size
console.log(myBlob.type + " is the type");
// Output: text/html is the type
"Пример 2. Создание больших двоичных объектов из типизированных массивов и строк"
let hello = new Uint8Array([72, 101, 108, 108, 111]); // 二进制格式的 "hello"
let blob = new Blob([hello, ' ', 'semlinker'], {type: 'text/plain'});
Познакомим с конструктором BLOB, давайте познакомим со свойствами и методами класса BLOB соответственно:
2.2.2 Свойства большого двоичного объекта
Ранее мы уже знали, что объект Blob содержит два свойства:
- размер (только для чтения): указывает
Blob
Размер в байтах данных, содержащихся в объекте. - тип (только для чтения): строка, указывающая
Blob
Тип MIME данных, которые содержит объект. Если тип неизвестен, значением является пустая строка.
2.2.3 Метод BLOB
- slice([start[ end[ contentType]]]): возвращает новый объект Blob, содержащий указанный диапазон данных в исходном объекте Blob.
- stream(): возвращает поток, который может читать содержимое большого двоичного объекта.
ReadableStream
. - text(): возвращает объект Promise, содержащий все содержимое большого двоичного объекта в формате UTF-8.
USVString
. - arrayBuffer(): возвращает объект Promise, содержащий все содержимое большого двоичного объекта в двоичном формате.
ArrayBuffer
.
Здесь мы должны отметить, что,"Blob
Объекты неизменны". Мы не можем изменять данные непосредственно в большом двоичном объекте, но мы можем разделить большой двоичный объект, создать из него новые объекты большого двоичного объекта и объединить их в новый большой двоичный объект. Это поведение похоже на строки JavaScript: мы не можем изменить символы в строке, но мы можем создать новую исправленную строку.
Для API выборкиResponseобъект, объект в дополнение к предоставлениюblob()
В дополнение к методу он также обеспечиваетjson()
,text()
,formData()
а такжеarrayBuffer()
и другие методы преобразования ответов в различные форматы данных.
В проекте, разделенном передним и задним концом, каждый должен быть более полезным.json()
Методы, в то время как другие методы могут быть относительно относительно небольшими. В предыдущем примере мы преобразуем объект ответа вArrayBuffer
объект, изображение, загруженное из сети, также может отображаться нормально, а конкретный код выглядит следующим образом:
<h3>阿宝哥:获取远程图片预览示例</h3>
<img id="previewContainer" style="width: 50%;"/>
<script>
const image = document.querySelector("#previewContainer");
fetch("https://avatars3.githubusercontent.com/u/4220799")
.then((response) => response.arrayBuffer())
.then((buffer) => {
const blob = new Blob([buffer]);
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
});
</script>
В приведенном выше коде мы сначала преобразуем объект ответа вArrayBuffer
объект, затем, вызвав конструктор Blob, поместитеArrayBuffer
Объекты преобразуются в объекты Blob и используются повторно.createObjectURL()
Метод создает URL-адрес объекта и, наконец, реализует предварительный просмотр изображения.
Я полагаю, что некоторые друзья еще не знакомы с ArrayBuffer, и теперь Брат А Бао возьмет вас, чтобы раскрыть его тайну.""вуаль"".
2.3 ArrayBuffer и TypedArray
2.3.1 ArrayBuffer
Объекты ArrayBuffer используются для представления"Универсальный, фиксированной длины"Буфер необработанных двоичных данных."ArrayBuffer нельзя манипулировать напрямую, но черезтип объекта массиваилиDataView
объект для работы", которые представляют данные в буфере в определенных форматах и используют эти форматы для чтения и записи содержимого буфера.
❝ArrayBuffer — это просто часть памяти, но вы не можете использовать ее напрямую. Это похоже на то, что если вы выделяете часть памяти в C, вы также преобразуете ее в unsigned_int32 или int16, которые являются фактическими типами массивов/указателей, которые вам нужно использовать.
Это роль TypedArray в JS, будь то Uint32Array или Int16Array, все они предоставляют «представление» для ArrayBuffer. Исходные слова в MDN называются «Несколько представлений одних и тех же данных» и их чтение с индексом. Записи в конечном итоге будут отражены в ArrayBuffer, на котором он построен.
Источник: https://www.zhihu.com/question/30401979
❞
"грамматика"
new ArrayBuffer(length)
- Параметры: length указывает размер создаваемого ArrayBuffer в байтах.
- Возвращаемое значение: объект ArrayBuffer указанного размера, содержимое которого инициализируется равным 0.
- Исключение: если длина больше
Number.MAX_SAFE_INTEGER
(>= 2**53) или отрицательный, бросьтеRangeError
аномальный.
"Пример"
В следующем примере создается 8-байтовый буфер и используетсяInt32Array
процитировать его:
let buffer = new ArrayBuffer(8);
let view = new Int32Array(buffer);
Начиная с ECMAScript 2015,ArrayBuffer
объект должен использоватьnew
создание оператора. Если конструктор вызывается без использованияnew
, броситTypeError
аномальный. Например, выполните операторlet ab = ArrayBuffer(10)
Будет выброшено следующее исключение:
VM109:1 Uncaught TypeError: Constructor ArrayBuffer requires 'new'
at ArrayBuffer (<anonymous>)
at <anonymous>:1:10
Для некоторых часто используемых веб-API, таких как FileReader API и Fetch API, ArrayBuffer также поддерживается внизу.Здесь мы берем FileReader API в качестве примера, чтобы увидеть, как читать объекты File как объекты ArrayBuffer:
const reader = new FileReader();
reader.onload = function(e) {
let arrayBuffer = reader.result;
}
reader.readAsArrayBuffer(file);
2.3.2 Unit8Array
Тип массива Uint8Array представляет собой массив 8-битных целых чисел без знака, содержимое которого инициализируется 0 при создании. После создания вы можете"Метод объекта или использование индексации индекса массива"Ссылка на элемент в массиве.
"грамматика"
new Uint8Array(); // ES2017 最新语法
new Uint8Array(length); // 创建初始化为0的,包含length个元素的无符号整型数组
new Uint8Array(typedArray);
new Uint8Array(object);
new Uint8Array(buffer [, byteOffset [, length]]);
"Пример"
// new Uint8Array(length);
var uint8 = new Uint8Array(2);
uint8[0] = 42;
console.log(uint8[0]); // 42
console.log(uint8.length); // 2
console.log(uint8.BYTES_PER_ELEMENT); // 1
// new TypedArray(object);
var arr = new Uint8Array([21,31]);
console.log(arr[1]); // 31
// new Uint8Array(typedArray);
var x = new Uint8Array([21, 31]);
var y = new Uint8Array(x);
console.log(y[0]); // 21
// new Uint8Array(buffer [, byteOffset [, length]]);
var buffer = new ArrayBuffer(8);
var z = new Uint8Array(buffer, 1, 4);
2.3.3 Связь между ArrayBuffer и TypedArray
Сам ArrayBuffer представляет собой просто строку из 0 и 1. ArrayBuffer не знает, где находится разделение между первым и вторым элементами в массиве.
(Источник изображения --A cartoon intro to ArrayBuffers and SharedArrayBuffers)
Чтобы предоставить контекст, фактически разбить его на блоки, нам нужно обернуть его в так называемое представление. Эти представления данных можно добавлять с помощью типизированных массивов, и вы можете использовать типизированные массивы самых разных типов.
Например, у вас может быть массив типа INT8, который разделит этот массив на массив из 8 бит.
(Источник изображения --A cartoon intro to ArrayBuffers and SharedArrayBuffers)
Или у вас может быть массив Int16 без знака, который разделит массив на 16-битные массивы байтов и обработает его как целое число без знака.
(Источник изображения --A cartoon intro to ArrayBuffers and SharedArrayBuffers)
Вы даже можете иметь несколько представлений в одном и том же базовом буфере. Различные представления дадут разные результаты для одной и той же операции.
Например, если мы получим значение элемента 0 и 1 (-19 и 100) из представления Int8 этого ArrayBuffer, оно даст нам другое значение, чем элемент 0 (25837) в представлении Uint16, даже если они содержат точно такие же биты.
(Источник изображения --A cartoon intro to ArrayBuffers and SharedArrayBuffers)
Таким образом, ArrayBuffer в основном как необработанная память. Этомитает прямой доступ к памяти, используя такие языки, как C."Вам может быть интересно, почему мы добавили этот уровень абстракции вместо того, чтобы позволить программам обращаться к памяти напрямую, поскольку прямой доступ к памяти привел бы к некоторым дырам в безопасности.".
Итак, мы узнали о Blob и ArrayBuffer, так в чем же между ними разница?
2.4 Blob vs ArrayBuffer
"ArrayBuffer"Объекты используются для представления общих буферов фиксированной длины необработанных двоичных данных. Вы не можете манипулировать содержимым ArrayBuffer напрямую, вместо этого вам нужно создать объект типизированного массива или объект DataView, представляющий буфер в определенном формате, и использовать этот объект для чтения и записи содержимого буфера.
"Blob"Объекты типа представляют собой неизменяемые файловые объекты примитивных данных. Большие двоичные объекты не обязательно представляют данные в собственном формате JavaScript. Интерфейс File основан на Blob, унаследовав функциональность Blob и расширив ее для поддержки файлов в системе пользователя.
2.4.1 Разница между Blob и ArrayBuffer
- Если вам не нужно использовать возможности записи/редактирования, предоставляемые ArrayBuffer, формат Blob, вероятно, является лучшим.
- Объекты BLOB-объектов являются неизменяемыми, а ArrayBuffers можно манипулировать с помощью TypedArrays или DataViews.
- ArrayBuffer хранится в памяти, и им можно манипулировать напрямую. Принимая во внимание, что большие двоичные объекты могут находиться на диске, в кэш-памяти и в других местах, которые недоступны.
- Хотя BLOB-объекты можно передавать напрямую в качестве аргументов другим функциям, например
window.URL.createObjectURL()
. Однако для работы с большими двоичными объектами вам по-прежнему могут понадобиться файловые API, такие как FileReader. - Объекты Blob и ArrayBuffer можно преобразовать друг в друга:
- Использование FileReader
readAsArrayBuffer()
метод, который может преобразовать объект Blob в объект ArrayBuffer; - Используйте конструктор Blob, например
new Blob([new Uint8Array(data]);
, вы можете преобразовать объект ArrayBuffer в объект Blob.
- Использование FileReader
Чтобы всем было легче понять процесс обращения, брат Абао просто приводит пример взаимного обращения:
2.4.2 Преобразование BLOB-объектов в ArrayBuffer
var blob = new Blob(["\x01\x02\x03\x04"]),
fileReader = new FileReader(),
array;
fileReader.onload = function() {
array = this.result;
console.log("Array contains", array.byteLength, "bytes.");
};
fileReader.readAsArrayBuffer(blob);
2.4.3 ArrayBuffer в большой двоичный объект
var array = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
var blob = new Blob([array]);
2.5 DataView и ArrayBuffer
Представление DataView — это низкоуровневый интерфейс, который может читать и записывать несколько числовых типов из бинарных объектов ArrayBuffer.При его использовании вам не нужно учитывать порядок байтов на разных платформах.
❝Порядок байта или последовательность, также известный как конечный Endian (English: Endianness), в области компьютерных наук, относится к порядку в памяти или цифровой связи, многобайтовое слово, состоящее из байтов.
Есть два общих правила расположения байтов. Например, для многобитового целого числа в байтах, отсортированных от младшего к старшему в соответствии с адресом хранения, если младший значащий байт целого числа (аналогичный младшему значащему биту) находится перед самым значащим байтом, это называется прямым порядком байтов; в противном случае он называется обратным порядком байтов. В сетевых приложениях порядок байтов является фактором, который необходимо учитывать, поскольку разные типы машин могут использовать разные стандартные порядки байтов, поэтому они преобразуются в соответствии с сетевыми стандартами.
Например, предположим, что указанная выше переменная
❞x
Типint
, по адресу0x100
, его значение0x01234567
, диапазон адресов0x100~0x103
Байты, внутренний порядок которых зависит от типа машины. Большой порядок байтов, начиная с первого, будет:0x100: 01, 0x101: 23,..
. И маленький порядок байтов будет:0x100: 67, 0x101: 45,..
.
2.5.1 Конструктор DataView
new DataView(buffer [, byteOffset [, byteLength]])
Соответствующие параметры описываются следующим образом:
- буфер: существующий объект ArrayBuffer или SharedArrayBuffer, источник данных объекта DataView.
- byteOffset (необязательно): байтовое смещение в буфере первого байта этого объекта DataView. Если не указано, по умолчанию используется первый байт.
- byteLength: длина в байтах этого объекта DataView. Если не указано, длина этого представления будет соответствовать длине буфера.
"Возвращаемое значение представления данных"
Вызов конструктора DataView с new возвращает новыйDataView
объект. Вы можете думать о возвращаемом объекте как об «интерпретаторе» для буфера массива двоичных байтовых буферов — он знает, как правильно преобразовывать байт-код при чтении или записи. Это означает, что он может обрабатывать целочисленные преобразования и преобразования с плавающей запятой, порядок байтов и другие детали на двоичном уровне.
"Пример использования DataView"
const buffer = new ArrayBuffer(16);
// Create a couple of views
const view1 = new DataView(buffer);
const view2 = new DataView(buffer, 12, 4); //from byte 12 for the next 4 bytes
view1.setInt8(12, 42); // put 42 in slot 12
console.log(view2.getInt8(0)); // expected output: 42
2.5.2 Свойства представления данных
Все экземпляры DataView наследуются от DataView.prototype и позволяют добавлять дополнительные свойства к объекту DataView.
- DataView.prototype.buffer (только для чтения): указывает на набор объектов ArrayBuffer при создании DataView;
- DataView.prototype.byteLength (только для чтения): указывает длину объекта ArrayBuffer или SharedArrayBuffer в байтах;
- DataView.prototype.byteOffset (только для чтения): указывает длину смещения в байтах при чтении из ArrayBuffer.
2.5.3 Методы представления данных
Объект DataView предоставляет методы getInt8(), getUint8(), setInt8() и setUint8() для управления данными. Конкретное использование каждого метода мы не будем вдаваться в подробности. Здесь мы рассмотрим простой пример:
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer, 0);
view.setInt8(1, 68);
view.getInt8(1); // 68
После ознакомления с соответствующими знаниями об ArrayBuffer, TypedArray и DataView брат Абао использует картинку, чтобы резюмировать взаимосвязь между ними.
Хорошо, давайте перейдем к следующей ссылке прямо сейчас.
3. Изображения в градациях серого
Чтобы отобразить изображение в градациях серого, нам нужно манипулировать данными пикселей изображения. Итак, вопрос в том, как нам получить пиксельные данные изображения?
3.1 Метод getImageData
В ответ на вышеуказанные проблемы мы можем использовать CanvasRenderingContext2D для предоставленияgetImageData
чтобы получить пиксельные данные изображения, где getImageData() возвращает объект ImageData, который используется для описания пиксельных данных, подразумеваемых областью холста.Эта область представлена прямоугольником, начальная точка (sx, sy), ширина sw, а высота sh. вgetImageData
Синтаксис метода следующий:
ctx.getImageData(sx, sy, sw, sh);
Соответствующие параметры описываются следующим образом:
- sx: x-координата левого верхнего угла прямоугольной области данных изображения, которые необходимо извлечь.
- sy: Y-координата левого верхнего угла прямоугольной области данных изображения, которые необходимо извлечь.
- sw: ширина прямоугольной области данных изображения, которые необходимо извлечь.
- sh: высота прямоугольной области данных изображения, которые необходимо извлечь.
3.2 метод putImageData
После получения пиксельных данных изображения мы можем обработать полученные пиксельные данные, такие как оттенки серого или инверсная обработка цвета. Когда обработка завершена, для отображения эффекта обработки на странице нам нужно использовать другой API, предоставляемый CanvasRenderingContext2D —putImageData
.
С помощью этого API Canvas 2D API рисует данные из существующего объекта ImageData в растровое изображение. Если предоставляется нарисованный прямоугольник, отрисовываются только пиксели этого прямоугольника. На этот метод не влияет матрица преобразования холста. Синтаксис метода putImageData следующий:
void ctx.putImageData(imagedata, dx, dy);
void ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
Соответствующие параметры описываются следующим образом:
- изображениеДанные:
ImageData
, объект массива, содержащий значения пикселей. - dx: смещение положения данных исходного изображения на целевом холсте (смещение в направлении оси X).
- dy: смещение положения данных исходного изображения на целевом холсте (смещение в направлении оси Y).
- dirtyX (необязательно): в данных исходного изображения положение левого верхнего угла прямоугольной области. По умолчанию используется верхний левый угол (координата x) всех данных изображения.
- dirtyY (необязательно): в данных исходного изображения положение левого верхнего угла прямоугольной области. По умолчанию используется верхний левый угол (координата Y) всех данных изображения.
- dirtyWidth (необязательно): в данных исходного изображения ширина прямоугольной области. По умолчанию используется ширина данных изображения.
- dirtyHeight (необязательно): высота прямоугольной области в данных исходного изображения. По умолчанию используется высота данных изображения.
3.3 Обработка изображений в градациях серого
ВведениеgetImageData()
а такжеputImageData()
метод, давайте посмотрим, как использовать их для получения изображения в градациях серого:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>获取远程图片并灰度化</title>
</head>
<body>
<h3>阿宝哥:获取远程图片并灰度化示例</h3>
<div>
<button id="grayscalebtn">灰度化</button>
<div style="display: flex;">
<div style="flex: 50%;">
<p>预览容器</p>
<img
id="previewContainer"
width="230"
height="230"
style="border: 2px dashed blue;"
/>
</div>
<div style="flex: 50%;">
<p>Canvas容器</p>
<canvas
id="canvas"
width="230"
height="230"
style="border: 2px dashed grey;"
></canvas>
</div>
</div>
</div>
<script>
const image = document.querySelector("#previewContainer");
const canvas = document.querySelector("#canvas");
fetch("https://avatars3.githubusercontent.com/u/4220799")
.then((response) => response.blob())
.then((blob) => {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
image.onload = () => {
draw();
};
});
function draw() {
const ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, 230, 230);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const grayscale = function () {
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
ctx.putImageData(imageData, 0, 0);
};
const grayscalebtn = document.querySelector("#grayscalebtn");
grayscalebtn.addEventListener("click", grayscale);
}
</script>
</body>
</html>
В приведенном выше примере мы сначала загружаем аватар Баоге с Github, затем предварительно просматриваем его локально, а затем вызываемdraw()
Метод рисует полученное изображение в контейнере Canvas страницы и привязывает событие прослушивателя для кнопки в градациях серого.
Когда пользователь нажимает кнопку оттенков серого, активируется функция обработки оттенков серого.ctx.getImageData()
Пиксели изображения, полученные методом, обрабатываются в оттенках серого, и после завершения обработкиctx.putImageData()
Метод обновляет обработанные данные пикселей в Canvas.
После успешного выполнения приведенного выше кода окончательный эффект оттенков серого показан на следующем рисунке:
В-четвертых, сжатие изображений
В некоторых случаях мы надеемся, что при загрузке локального изображения изображение сначала сжимается до определенной степени, а затем отправляется на сервер, тем самым уменьшая объем передаваемых данных. Чтобы добиться сжатия изображения во внешнем интерфейсе, мы можем использовать предоставленный объект Canvas.toDataURL()
метод, который получаетtype
а такжеencoderOptions
Два необязательных параметра.
вtype
Указывает формат изображения, по умолчаниюimage/png
. а такжеencoderOptions
Используется для обозначения качества изображения в указанном формате изображения.image/jpeg
илиimage/webp
В случае качество изображения можно выбрать в диапазоне от 0 до 1. Если оно превышает диапазон значений, будет использоваться значение по умолчанию.0.92
, другие параметры игнорируются.
Давайте посмотрим, как сжать изображение, которое ранее было окрашено в оттенки серого.
<button id="compressbtn">图片压缩</button>
<div style="display: flex;">
<div style="flex: 33.3%;">
<p>预览容器</p>
<img id="previewContainer" width="230" height="230"
style="border: 2px dashed blue;" />
</div>
<div style="flex: 33.3%;">
<p>Canvas容器</p>
<canvas id="canvas" width="230" height="230"
style="border: 2px dashed grey;">
</canvas>
</div>
<div style="flex: 33.3%;">
<p>压缩预览容器</p>
<img id="compressPrevContainer" width="230" height="230"
style="border: 2px dashed green;" />
</div>
</div>
<script>
const compressbtn = document.querySelector("#compressbtn");
const compressImage = document.querySelector("#compressPrevContainer");
compressbtn.addEventListener("click", compress);
function compress(quality = 80, mimeType = "image/webp") {
const imageDataURL = canvas.toDataURL(mimeType, quality / 100);
compressImage.src = imageDataURL;
}
</script>
В приведенном выше коде мы устанавливаем качество изображения по умолчанию равным"0.8", а тип изображения"image/webp"Типы. Когда пользователь нажимает кнопку сжатия, объект CanvastoDataURL()
метод достижения сжатия изображения. После успешного выполнения приведенного выше кода окончательный результат обработки показан на следующем рисунке:
На самом деле объект Canvas предоставляет в дополнение кtoDataURL()
метод, он также обеспечиваетtoBlob()
метод, синтаксис этого метода следующий:
canvas.toBlob(callback, mimeType, qualityArgument)
а такжеtoDataURL()
По сравнению с методом,toBlob()
Метод асинхронный, поэтомуcallback
параметр, этоcallback
Первый параметр метода обратного вызова по умолчанию — преобразованный.blob
информация о файле.
5. Загрузка изображения
После получения данных URL-адреса данных, соответствующих сжатому изображению, данные можно напрямую отправить на сервер. Ввиду этой ситуации серверу необходимо выполнить некоторую связанную обработку, чтобы нормально сохранить загруженные изображения.ExpressНапример, конкретный код обработки выглядит следующим образом:
const app = require('express')();
app.post('/upload', function(req, res){
let imgData = req.body.imgData; // 获取POST请求中的base64图片数据
let base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
let dataBuffer = Buffer.from(base64Data, 'base64');
fs.writeFile("abao.png", dataBuffer, function(err) {
if(err){
res.send(err);
}else{
res.send("图片上传成功!");
}
});
});
Однако данные изображения в возвращаемом формате URL-адреса данных обычно относительно велики. Чтобы еще больше уменьшить объем передаваемых данных, мы можем преобразовать их в объект Blob:
function dataUrlToBlob(base64, mimeType) {
let bytes = window.atob(base64.split(",")[1]);
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: mimeType });
}
После завершения преобразования мы можем инкапсулировать объект Blob, соответствующий сжатому изображению, в объект FormData, а затем отправить его на сервер через AJAX:
function uploadFile(url, blob) {
let formData = new FormData();
let request = new XMLHttpRequest();
formData.append("imgData", blob);
request.open("POST", url, true);
request.send(formData);
}
6. Брату А Бао есть что сказать
6.1 Как просмотреть двоичные данные изображения
Для просмотра бинарных данных, соответствующих картинке, нам нужно использовать некоторые готовые редакторы, например под платформу Windows"WinHex"или под платформу macOS"Synalyze It! Pro"Шестнадцатеричный редактор. Здесь мы используем редактор Synalyze It!Pro для просмотра двоичных данных, соответствующих аватару Брата Абао, в шестнадцатеричном виде.
6.2 Как различать типы изображений
"Компьютер различает разные типы изображений не по суффиксу имени изображения, а по «магическому номеру»."Для некоторых типов файлов содержимое первых нескольких байтов фиксировано, и по содержимому этих байтов можно судить о типе файла.
Магические числа, соответствующие распространенным типам изображений, показаны в следующей таблице:
тип файла | Расширение файла | магическое число |
---|---|---|
JPEG | jpg/jpeg | 0xFFD8FF |
PNG | png | 0x89504E47 |
GIF | gif | 0x47494638 (GIF8) |
BMP | bmp | 0x424D |
Здесь мы берем аватар брата Абао (abao.png) в качестве примера, чтобы проверить, имеет ли изображение правильный тип:
В процессе ежедневной разработки, если мы сталкиваемся со сценой обнаружения типов изображений, мы можем напрямую использовать некоторые готовые сторонние библиотеки. Например, если вы хотите определить, относится ли изображение к типу PNG, вы можете использоватьis-pngЭта библиотека, которая поддерживает как браузеры, так и Node.js, использует следующие примеры:
"Node.js"
// npm install read-chunk
const readChunk = require('read-chunk');
const isPng = require('is-png');
const buffer = readChunk.sync('unicorn.png', 0, 8);
isPng(buffer);
//=> true
"Browser"
(async () => {
const response = await fetch('unicorn.png');
const buffer = await response.arrayBuffer();
isPng(new Uint8Array(buffer));
//=> true
})();
6.3 Как получить размер изображения
Размер, битовая глубина, тип цвета и алгоритм сжатия изображения хранятся в двоичных данных файла.Давайте продолжим использовать аватар брата Абао (abao.png) в качестве примера, чтобы понять реальную ситуацию:
❝528 (десятичное) => 0x0210 (шестнадцатеричное)
560 (десятичное) => 0x0230 (шестнадцатеричное)
❞
Поэтому, если мы хотим получить размер изображения, нам нужно проанализировать двоичные данные изображения в соответствии с различными форматами изображения. К счастью, нам не нужно реализовывать эту функциональность самостоятельно,image-sizeЭта библиотека Node.js помогла нам реализовать функцию получения размера файла основных типов изображений.Примеры использования следующие:
"Синхронно"
var sizeOf = require('image-size');
var dimensions = sizeOf('images/abao.png');
console.log(dimensions.width, dimensions.height);
"Асинхронный способ"
var sizeOf = require('image-size');
sizeOf('images/abao.png', function (err, dimensions) {
console.log(dimensions.width, dimensions.height);
});
image-sizeЭта библиотека достаточно мощная, помимо поддержки форматов PNG, она также поддерживает такие форматы, как BMP, GIF, ICO, JPEG, SVG и WebP.
6.4 Как декодировать пиксельные данные в изображениях PNG
Я полагаю, что ребята также слышали о декодировании изображений, аудио и видео декодировании. Декодирование изображения PNG преобразует изображение из двоичных данных в ImageData, содержащее пиксельные данные. Как мы уже говорили ранее, вы можете использовать CanvasRenderingContext2D для предоставленияgetImageData()
метод для получения данных о пикселях изображения.
ТакgetImageData()
Как это обрабатывается внутри метода? Давайте кратко представим общий процесс.В качестве примера мы возьмем изображение размером 2px * 2px.На следующем рисунке показан эффект увеличенного отображения:
(Источник изображения: https://vivaxyblog.github.io/2019/12/07/decode-a-png-image-with-javascript-cn.html)
Точно так же мы сначала используем"Synalyze It! Pro"Шестнадцатеричный редактор открывает вышеуказанный"2px * 2px"картинка:
Пиксельные данные изображения PNG хранятся в"IDAT"Блок, кроме"IDAT"В дополнение к блоку, он также содержит другие блоки данных.Полный блок данных выглядит следующим образом:
(Источник изображения: https://dev.gameres.com/Program/Visual/Other/PNGFormat.htm)
Прежде чем анализировать пиксельные данные, давайте разберемся, как кодируются пиксельные данные. Каждая строка пикселей сначала обрабатывается функцией фильтра, и функция фильтрации каждой строки пикселей может быть разной. Затем будут проходить пиксельные данные всех строк."deflate"Алгоритм сжатия Сжатие. Здесь Апоге используетpakoЭта библиотека выполняет операции декодирования:
const pako = require("pako");
const compressed = new Uint8Array([120, 156, 99, 16, 96, 216, 0, 0, 0, 228, 0, 193]);
try {
const result = pako.inflate(compressed);
console.dir(result);
} catch (err) {
console.log(err);
}
В приведенном выше коде, позвонивpako.inflate()
Метод выполняет операцию распаковки, и окончательные данные распакованного пикселя выглядят следующим образом:
Uint8Array [ 0, 16, 0, 176 ]
После получения распакованных данных пикселей необходимо декодировать строку сканирования, а затем восстановить информацию о пикселях изображения в соответствии с индексом в образце. Брат Абао не будет здесь подробно раскрываться, заинтересованные друзья могут прочитатьШаг за шагом декодировать изображение PNGЭта статья.
6.5 Как загружать большие файлы сегментами
Файловые объекты представляют собой особый тип BLOB-объектов и могут использоваться в контексте любого типа BLOB-объектов. Таким образом, для сценария передачи большого файла мы можем использовать метод среза, чтобы разрезать большой файл, а затем загрузить его по частям. Конкретный пример выглядит следующим образом:
const file = new File(["a".repeat(1000000)], "test.txt");
const chunkSize = 40000;
const url = "https://httpbin.org/post";
async function chunkedUpload() {
for (let start = 0; start < file.size; start += chunkSize) {
const chunk = file.slice(start, start + chunkSize + 1);
const fd = new FormData();
fd.append("data", chunk);
await fetch(url, { method: "post", body: fd }).then((res) =>
res.text()
);
}
}
6.6 Как реализовать загрузку файла
В некоторых сценариях мы будем использовать Canvas для редактирования изображений или использоватьjsPDF,sheetjsПодождите, пока некоторые сторонние библиотеки обработают документы.Когда файл будет обработан, нам нужно скачать и сохранить файл локально. Для этих сценариев мы можем использовать чисто внешнее решение для загрузки файлов.
«Разговоры дешевы», — приводит пример простой загрузки Blob-файла брат Бао:
"index.html"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Blob 文件下载示例</title>
</head>
<body>
<button id="downloadBtn">文件下载</button>
<script src="index.js"></script>
</body>
</html>
"index.js"
const download = (fileName, blob) => {
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = fileName;
link.click();
link.remove();
URL.revokeObjectURL(link.href);
};
const downloadBtn = document.querySelector("#downloadBtn");
downloadBtn.addEventListener("click", (event) => {
const fileName = "blob.txt";
const myBlob = new Blob(["一文彻底掌握 Blob Web API"], { type: "text/plain" });
download(fileName, myBlob);
});
В примере мы создаем тип Blob, вызывая конструктор Blob""text/plain""Затем объект Blob динамически создаетсяa
теги для загрузки файлов. В реальном процессе разработки проекта мы можем использовать зрелые библиотеки с открытым исходным кодом, такие какFileSaver.jsреализовать функцию сохранения файла.
7. Справочные ресурсы
- PNGFormat
- w3.org - PNG
- вики - порядок следования байтов
- Шаг за шагом декодировать изображение PNG
- comprehensive-image-processing-on-browsers
❞
В этой статье используетсяmdniceнабор текста