Капли, о которых вы не знали

внешний интерфейс JavaScript
Капли, о которых вы не знали

Серии, о которых вы не знали:

WeakMap, которого вы не знаете

1. Что такое блоб

Blob (Binary Large Object) представляет собой большой объект двоичного типа. В системе управления базами данных двоичные данные хранятся как набор одного объекта. Блобы обычно представляют собой изображения, звуковые или мультимедийные файлы.Объекты типа Blob в JavaScript представляют собой неизменяемые файловоподобные примитивные данные.Чтобы более интуитивно чувствовать объект Blob, давайте сначала воспользуемся конструктором Blob для создания объекта myBlob, как показано на следующем рисунке:

plain-type-blob

Как видите, объект myBlob имеет два свойства: размер и тип. вsizeатрибут используется для представления размера данных в байтах,type是 MIME 类型的字符串。 Blob 表示的不一定是 JavaScript 原生格式的数据。 НапримерFileинтерфейс на основеBlob, который наследует функциональные возможности BLOB-объектов и расширяет их для поддержки файлов в системе пользователя.

2. Введение в API BLOB-объектов

Blobопциональной строкойtype(обычно тип MIME) иblobPartsсочинение:

blob-structure

MIME (многоцелевые расширения почты Интернета) — это тип многоцелевого расширения почты Интернета, который представляет собой способ установить файл с определенным расширением для открытия приложением.При доступе к файлу расширения браузер будет автоматически использовать указанный расширение приложение для открытия. Он в основном используется для указания некоторых определяемых клиентом имен файлов, а также некоторых методов открытия медиафайлов.

Общие типы MIME: текст HTML .html текст / html, изображение PNG .png изображение / png, обычный текст .txt текст / обычный и так далее.

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:

plain-type-blob-proto

2.2 Свойства

Ранее мы уже знали, что объект Blob содержит два свойства:

  • размер (только для чтения): указываетBlobРазмер в байтах данных, содержащихся в объекте.
  • тип (только для чтения): строка, указывающаяBlobТип MIME данных, которые содержит объект. Если тип неизвестен, значением является пустая строка.

2.3 Методы

  • slice([start[ end[ contentType]]]): возвращает новый объект Blob, содержащий указанный диапазон данных в исходном объекте Blob.
  • stream(): возвращает поток, который может читать содержимое большого двоичного объекта.ReadableStream.
  • text(): возвращает объект Promise, содержащий все содержимое большого двоичного объекта в формате UTF-8.USVString.
  • arrayBuffer(): возвращает объект Promise, содержащий все содержимое большого двоичного объекта в двоичном формате.ArrayBuffer.

Здесь мы должны отметить, что,BlobОбъекты неизменны. Мы не можем изменять данные непосредственно в большом двоичном объекте, но мы можем разделить большой двоичный объект, создать из него новые объекты большого двоичного объекта и объединить их в новый большой двоичный объект. Это поведение похоже на строки JavaScript: мы не можем изменить символы в строке, но мы можем создать новую исправленную строку.

3. Сценарии использования BLOB-объектов

3.1 Частичная загрузка

Файловые объекты представляют собой особый тип 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()
      );
  }
}

3.2 Загрузка данных из Интернета

Мы можем загружать данные из Интернета и хранить их в объекте Blob, используя:

const downloadBlob = (url, callback) => {
	const xhr = new XMLHttpRequest()
	xhr.open('GET', url)
	xhr.responseType = 'blob'
	xhr.onload = () => {
    callback(xhr.response)
	}
	xhr.send(null)
}

Конечно, кроме использованияXMLHttpRequestВ дополнение к API мы также можем использоватьfetchAPI для достижения потока доступа к двоичным данным. Здесь, давайте посмотрим, как использовать Petch API, чтобы получить онлайн-изображения и отображать их локально. Конкретная реализация выглядит следующим образом:

const myImage = document.querySelector('img');
const myRequest = new Request('flowers.jpg');

fetch(myRequest)
  .then(function(response) {
    return response.blob();
  })
 .then(function(myBlob) {
   let objectURL = URL.createObjectURL(myBlob);
   myImage.src = objectURL;
});

Когда запрос выборки выполнен успешно, мы вызываем метод объекта ответа.blob()метод, прочитайте объект Blob из объекта ответа, а затем используйтеcreateObjectURL()метод создает objectURL и присваивает егоimgэлементальsrcсвойство для отображения этого изображения.

3.3 Большой двоичный объект как URL-адрес

Блобы можно легко использовать как<a>,<img>или URL других тегов, благодаряtypeсвойств, мы также можем загружать/загружатьBlobобъект. Ниже мы приведем пример загрузки файла BLOB-объекта, но прежде чем мы рассмотрим конкретный пример, нам нужно кратко представить URL-адрес BLOB-объекта.

1.Blob URL/Object URL

URL-адрес BLOB-объекта/URL-адрес объекта — это псевдопротокол, который позволяет использовать объекты 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-адрес → Карта BLOB-объектов. Поэтому такие URL короче, но к ним можно получить доступBlob. Сгенерированный URL-адрес действителен только в том случае, если текущий документ открыт. это позволяет цитировать<img>,<a>серединаBlob, но если вы перейдете по URL-адресу большого двоичного объекта, которого больше не существует, вы получите ошибку 404 в своем браузере.

Приведенный выше URL-адрес большого двоичного объекта выглядит великолепно, но на самом деле он имеет побочные эффекты. Хотя сопоставление URL-адрес → BLOB-объект сохраняется, сам BLOB-объект все еще находится в памяти, и браузер не может его освободить. Сопоставление автоматически очищается, когда документ выгружается, поэтому объект Blob затем освобождается. Однако, если приложение является долгоживущим, это не произойдет в ближайшее время. Таким образом, если мы создадим URL-адрес большого двоичного объекта, он будет существовать в памяти, даже если этот большой двоичный объект больше не нужен.

Для этой проблемы мы можем вызватьURL.revokeObjectURL(url)удаляет ссылку из внутренней карты, позволяя удалить большой двоичный объект, если нет других ссылок, и освобождает память. Далее давайте рассмотрим конкретный пример загрузки файла большого двоичного объекта.

2. Пример загрузки 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теги для загрузки файлов.

3.4 Преобразование BLOB-объектов в Base64

URL.createObjectURLАльтернатива - поставитьBlobПреобразование в строку в кодировке base64.Base64представляет собой представление двоичных данных, основанное на 64 печатных символах,Он часто используется для представления, передачи и хранения некоторых двоичных данных, включая сообщения электронной почты MIME и некоторые сложные данные в формате XML, при работе с текстовыми данными.

В электронной почте в формате MIME base64 может использоваться для кодирования данных последовательности двоичных байтов в текст, состоящий из последовательности символов ASCII. При использовании укажите base64 в методе кодирования передачи. Используемые символы включают 26 прописных и строчных латинских букв, 10 цифр, знак плюс + и косую черту /, всего 64 символа, а знак равенства = используется в качестве суффикса.

下面我们来介绍如何在 HTML 中嵌入 base64 编码的图片。 При написании веб-страниц в формате HTML для некоторых простых изображений обычно выбирают прямое встраивание содержимого изображения в веб-страницу, тем самым уменьшая ненужные сетевые запросы, но данные изображения являются двоичными данными, как их встроить? Большинство современных браузеров поддерживаютData URLsФункция, позволяющая кодировать двоичные данные изображений или других файлов с использованием base64 для встраивания в веб-страницы в виде текстовых строк.

URL-адреса данных состоят из четырех частей: префикса (data:), тип MIME, указывающий тип данных, необязательный, если нетекстовыйbase64Тег, сами данные:

data:[<mediatype>][;base64],<data>

mediatypeявляется строкой типа MIME, например, "image/jpeg" означает файл изображения в формате JPEG. Если этот параметр опущен, по умолчаниюtext/plain;charset=US-ASCII. Если данные имеют текстовый тип, вы можете внедрить текст напрямую (используя соответствующие символы сущности или escape-символы в зависимости от типа документа). Если это двоичные данные, вы можете закодировать данные в base64 перед встраиванием. Например, чтобы вставить изображение:

<img alt="logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...">

Однако следует отметить, что если изображение большое и цветовой уровень изображения насыщенный, использовать этот метод нецелесообразно, так как строка, закодированная base64 изображения, очень велика, что значительно увеличит размер HTML-страницы, тем самым влияя на размер HTML-страницы, скорость загрузки.Кроме того, используя FileReader API, мы также можем легко реализовать функцию предварительного просмотра локального изображения.Конкретный код выглядит следующим образом:

<input type="file" accept="image/*" onchange="loadFile(event)">
<img id="output"/>

<script>
  const loadFile = function(event) {
    const reader = new FileReader();
    reader.onload = function(){
      const output = document.querySelector('output');
      output.src = reader.result;
    };
    reader.readAsDataURL(event.target.files[0]);
  };
</script>

В приведенном выше примере мы привязываем поле ввода типа файлаonchangeобработчик событияloadFile, в этой функции мы создаем объект FileReader и привязываемся к этому объектуonloadсоответствующий обработчик событий, а затем вызвать объект FileReaderreadAsDataURL()метод для преобразования объекта File, соответствующего локальному изображению, в URL-адрес данных.

После завершения превью локального изображения мы можем напрямую отправить данные 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("image.png", dataBuffer, function(err) {
        if(err){
          res.send(err);
        }else{
          res.send("图片上传成功!");
        }
    });
});

Для объектов FileReader, помимо поддержки преобразования объектов Blob/File в URL-адреса данных, он также обеспечиваетreadAsArrayBuffer()а такжеreadAsText()Методы преобразования объектов Blob/File в другие форматы данных. Здесь мы смотрим наreadAsArrayBuffer()Пример использования:

// 从 blob 获取 arrayBuffer
let fileReader = new FileReader();

fileReader.onload = function(event) {
  let arrayBuffer = fileReader.result;
};
fileReader.readAsArrayBuffer(blob);

3.5 Сжатие изображения

В некоторых случаях мы надеемся, что при загрузке локального изображения изображение сначала сжимается до определенной степени, а затем отправляется на сервер, тем самым уменьшая объем передаваемых данных. Чтобы добиться сжатия изображения во внешнем интерфейсе, мы можем использовать предоставленный объект Canvas.toDataURL()метод, который получаетtypeа такжеencoderOptionsДва необязательных параметра.

вtypeУказывает формат изображения, по умолчаниюimage/png. а такжеencoderOptionsИспользуется для обозначения качества изображения в указанном формате изображения.image/jpegилиimage/webpВ случае качество изображения можно выбрать в диапазоне от 0 до 1. Если оно превышает диапазон значений, будет использоваться значение по умолчанию.0.92, другие параметры игнорируются.

Давайте посмотрим, как добиться сжатия изображения:

// compress.js
const MAX_WIDTH = 800; // 图片最大宽度

function compress(base64, quality, mimeType) {
  let canvas = document.createElement("canvas");
  let img = document.createElement("img");
  img.crossOrigin = "anonymous";
  return new Promise((resolve, reject) => {
    img.src = base64;
    img.onload = () => {
      let targetWidth, targetHeight;
      if (img.width > MAX_WIDTH) {
        targetWidth = MAX_WIDTH;
        targetHeight = (img.height * MAX_WIDTH) / img.width;
      } else {
        targetWidth = img.width;
        targetHeight = img.height;
      }
      canvas.width = targetWidth;
      canvas.height = targetHeight;
      let ctx = canvas.getContext("2d");
      ctx.clearRect(0, 0, targetWidth, targetHeight); // 清除画布
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      let imageData = canvas.toDataURL(mimeType, quality / 100);
      resolve(imageData);
    };
  });
}

Для возвращаемых данных изображения в формате Data 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("image", blob);
  request.open("POST", url, true);
  request.send(formData);
}

На самом деле объект Canvas предоставляет в дополнение кtoDataURL()метод, он также обеспечиваетtoBlob()метод, синтаксис этого метода следующий:

canvas.toBlob(callback, mimeType, qualityArgument)

а такжеtoDataURL()По сравнению с методом,toBlob()Метод асинхронный, поэтомуcallbackпараметр, этоcallbackПервый параметр метода обратного вызова по умолчанию — преобразованный.blobинформация о файле.

После введения вышеизложенного давайте взглянем на полный пример локального сжатия изображения:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>本地图片压缩</title>
  </head>
  <body>
    <input type="file" accept="image/*" onchange="loadFile(event)" />
    <script src="./compress.js"></script>
    <script>
      const loadFile = function (event) {
        const reader = new FileReader();
        reader.onload = async function () {
          let compressedDataURL = await compress(
            reader.result,
            90,
            "image/jpeg"
          );
          let compressedImageBlob = dataUrlToBlob(compressedDataURL);
          uploadFile("https://httpbin.org/post", compressedImageBlob);
        };
        reader.readAsDataURL(event.target.files[0]);
      };
    </script>
  </body>
</html>

3.6 Создание PDF-документов

PDF (Portable Document Format) — это формат файлов, разработанный Adobe Systems в 1993 году для обмена документами. На стороне браузера используйте готовые библиотеки с открытым исходным кодом, такие какjsPDF, мы также можем легко создавать PDF-документы.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>客户端生成 PDF 示例</title>
  </head>
  <body>
    <h3>客户端生成 PDF 示例</h3>
    <script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>
    <script>
      (function generatePdf() {
        const doc = new jsPDF();
        doc.text("Hello semlinker!", 66, 88);
        const blob = new Blob([doc.output()], { type: "application/pdf" });
        blob.text().then((blobAsText) => {
          console.log(blobAsText);
        });
      })();
    </script>
  </body>
</html>

В приведенном выше примере мы сначала создаем объект документа PDF, а затем вызываемtext()Метод добавляет по указанной координатной точкеHello semlinker!текст, а затем мы используем сгенерированное содержимое PDF для создания соответствующего объекта Blob, следует отметить, что мы устанавливаем тип Blob наapplication/pdf, и, наконец, мы преобразуем содержимое, сохраненное в объекте Blob, в текст и выводим на консоль. Из-за большого количества контента здесь мы приводим только несколько выходных результатов:

%PDF-1.3
%ºß¬à
3 0 obj
<</Type /Page
/Parent 1 0 R
/Resources 2 0 R
/MediaBox [0 0 595.28 841.89]
/Contents 4 0 R
>>
endobj
....

фактическиjsPDFПомимо поддержки обычного текста, он также может создавать PDF-документы с изображениями, такими как:

let imgData = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/...'
let doc = new jsPDF();

doc.setFontSize(40)
doc.text(35, 25, 'Paranyan loves jsPDF')
doc.addImage(imgData, 'JPEG', 15, 40, 180, 160)

Сценариев применения Blob много, мы не будем перечислять их здесь по одному, заинтересованные партнеры могут сами проверить актуальную информацию.

В-четвертых, разница между Blob и ArrayBuffer

ArrayBufferОбъекты используются для представления общих буферов фиксированной длины необработанных двоичных данных. Вы не можете напрямую манипулировать содержимым ArrayBuffer, вместо этого вам нужно создать объект типизированного массива или объект DataView, представляющий буфер в определенном формате, и использовать этот объект для чтения и записи содержимого буфера.

BlobОбъекты типа представляют собой неизменяемые файловые объекты примитивных данных. Большие двоичные объекты не обязательно представляют данные в собственном формате JavaScript. Интерфейс File основан на Blob, унаследовав функциональность Blob и расширив ее для поддержки файлов в системе пользователя.

4.1 Blob vs ArrayBuffer

  • Если вам не нужно использовать возможности записи/редактирования, предоставляемые ArrayBuffer, формат Blob, вероятно, является лучшим.
  • Объекты BLOB-объектов являются неизменяемыми, а ArrayBuffers можно манипулировать с помощью TypedArrays или DataViews.
  • ArrayBuffer хранится в памяти, и им можно манипулировать напрямую. Принимая во внимание, что большие двоичные объекты могут находиться на диске, в кэш-памяти и в других местах, которые недоступны.
  • Хотя BLOB-объекты можно передавать напрямую в качестве аргументов другим функциям, напримерwindow.URL.createObjectURL(). Однако для работы с большими двоичными объектами вам по-прежнему могут понадобиться файловые API, такие как FileReader.
  • Объекты Blob и ArrayBuffer можно преобразовать друг в друга:
    • Использование FileReaderreadAsArrayBuffer()метод, который может преобразовать объект Blob в объект ArrayBuffer;
    • Используйте конструктор Blob, напримерnew Blob([new Uint8Array(data]);, вы можете преобразовать объект ArrayBuffer в объект Blob.

Для сценариев HTTP, таких как сценарии AJAX,Blobа такжеArrayBufferЕго можно использовать следующими способами:

function GET(url, callback) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.responseType = 'arraybuffer'; // or xhr.responseType = "blob";
  xhr.send();

  xhr.onload = function(e) {
    if (xhr.status != 200) {
      alert("Unexpected status code " + xhr.status + " for " + url);
      return false;
    }
    callback(new Uint8Array(xhr.response)); // or new Blob([xhr.response]);
  };
}

Поняв вышеизложенное, я полагаю, что некоторые читатели могут почувствовать, что они все еще не удовлетворены. Итак, о чем еще Blob может узнать больше? Мой следующий план — постепенно анализировать конкретную реализацию DenoBlob на основе исходного кода Deno. Конечно, мы также проанализируемURL.createObjectURL()Методы иrevokeObjectURL()реализация метода.

5. Справочные ресурсы