В те годы «Multiple Image Alert» проснулись программисты, которых злоупотребляли блобами!

JavaScript
В те годы «Multiple Image Alert» проснулись программисты, которых злоупотребляли блобами!

предисловие

Эта статья объясняет концепцию двоичного файла простым способом с помощью изображений и текстов.Предыдущие описания концепции скучны, но очень важны! Надеюсь, все будут терпеливо смотреть вниз, сзади будут сюрпризы, которые точно заставят вас трепетать~😉

Blob

BlobПредставляет большой объект двоичного типа, обычно файл изображения, звука или мультимедиа. В javaScript Blob представляет собой неизменяемый примитивный объект, похожий на файл данных.

Его конструктор выглядит следующим образом:

new Blob(blobParts, options);
  • lobParts: Тип массива, может хранить любое количествоArrayBuffer, ArrayBufferView, BlobилиDOMString(будут закодированы как UTF-8), объедините их, чтобы сформировать данные объекта Blob.
  • options: необязательный, используется для установки свойств объекта blob, вы можете указать следующие два свойства:
    • тип: содержимое массива, хранящегося в большом двоичном объектеMIMEТип (по умолчанию "").
    • окончания: используется для указания того, как записываются строки, содержащие окончания строк \n. значениеnativeУказывает, что признак конца строки будет изменен на символ новой строки, подходящий для файловой системы основной операционной системы (по умолчаниюtransparentУказывает, что терминатор, сохраненный в большом двоичном объекте, останется неизменным)
DOMString 是一个UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString直接映射到 一个String。
ArrayBuffer(二进制数据缓冲区)、ArrayBufferView(二进制数据缓冲区的array-like视图)

Пример ниже👇

  1. Создайте объект большого двоичного объекта, содержащий объект domstring.
    const blob = new Blob(['<div>john</div>'], { type: 'text/xml' });
    console.log(blob); // Blob {size: 15, type: "text/xml"}
  1. Создайте объект большого двоичного объекта, содержащий объекты массива буферов.
    var abf = new ArrayBuffer(8);
    const blob = new Blob([abf], { type: 'text/plain' });
    console.log(blob); // Blob {size: 8, type: "text/plain"}
  1. Создайте объект большого двоичного объекта, содержащий объекты arraybufferview.
    var abf = new ArrayBuffer(8);
    var abv = new Int16Array(abf);
    const blob = new Blob(abv, { type: 'text/plain' });
    console.log(blob); // Blob {size: 4, type: "text/plain"}

Атрибуты

Объект Blob имеет два свойства, см. следующую таблицу👇:

Имя свойства описывать
size Размер данных, содержащихся в объекте Blob. Байт - это единица измерения. Только чтение.
type Строка, указывающая тип MIME данных, содержащихся в этом объекте большого двоичного объекта. Если тип неизвестен, значением является пустая строка. Только чтение.

метод

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

Сохраните двоичный файл большого двоичного объекта (или файла) вformDataСделайте сетевой запрос (URL-адрес изображения, который можно получить позже, можно использовать для отображения изображения или последующей передачиwebsocketпришлите адрес фото)

File

FileОбъект представляет собой специальный объект Blob, который наследует все свойства и методы Blob.Конечно, он также может использоваться в качестве загрузки бинарного файла formData.

Получение файла:

Ниже мы используем методы ввода и перетаскивания для выбора нескольких изображений соответственно👇:

  • ввод получить локальный файл
  <input type="file" multiple id="f" />
  <script>
    var elem = document.getElementById('f');
    elem.onchange = function (event) {
      var files = event.target.files;
      console.log(files); // [{{name: "1.jpg",lastModified: 1594369580771...},{name:'2.jpg',lastModified: 1596012406708...}]
      var file = files[0];
      console.log(file); // {name: "1.jpg",lastModified: 1594369580771,size: 22344,type: "image/jpeg"...}
      console.log(file instanceof File); //true
      console.log(files instanceof FileList); // true
      
      /* File继承Blob */
      console.log(file.__proto__.__proto__); // Blob {size: 22344, type: ""}
    };
  </script>
  • Перетащите приобретение
    <div id="content" ondrop="drop(event)" ondragover="allowDrop(event);" />
    <script>
      function allowDrop(ev) {
        ev.preventDefault();
      }
      function drop(ev) {
        ev.preventDefault();
        const files = ev.dataTransfer.files;
        console.log(files); // [{{name: "1.jpg",lastModified: 1594369580771...},{name:'2.jpg',lastModified: 1596012406708...}]
        console.log(files instanceof FileList); // true
      }
    </script>
    <style type="text/css">
      #content {
        width: 500px;
        height: 500px;
        border: 1px solid brown;
      }
    </style>
    

добавить к элементу вводаmultipleАтрибут позволяет пользователю выбирать несколько файлов.Каждый файл, выбранный пользователем, является файловым объектом, а объект FileList представляет собой список этих файловых объектов, представляющих все файлы, выбранные пользователем, который представляет собой набор файловых объектов.

Атрибуты

Свойства файлового объекта см. ниже:

Имя свойства описывать
lastModified Дата последнего изменения ссылочного документа
name имя файла или путь к файлу
size Возвращает размер файла в байтах
type MIME-тип файла

метод

Объект File не имеет собственного метода экземпляра, поскольку он наследуется от объекта Blob, поэтому можно использовать метод slice() экземпляра Blob.

буфер данных

отXHR,File API,CanvasВ разных местах я читаю большую строку байтовых потоков, если я использую массив в JS для ее хранения, это расточительно и неэффективно. В программировании,数据缓冲区(или просто буфер) — это область хранения в физической памяти для манипулирования двоичными данными (быстрее, чем доступ к жесткому диску), используемая для хранения временных данных при их перемещении из одного места в другое,解释器Строки считываются с помощью буфера памяти, в котором хранятся двоичные данные. В основной памяти есть работающий файл, и если интерпретатору придется вернуться к файлу, чтобы прочитать каждый бит, выполнение займет много времени. Чтобы предотвратить это, JavaScript использует буфер данных, который хранит некоторые биты вместе, а затем отправляет все биты вместе интерпретатору. Таким образом, интерпретатору JavaScript не нужно беспокоиться об извлечении файла из файловых данных. Такой подход экономит время выполнения и ускоряет работу приложения. Различные классы буферов выполняют эффективные бинарные операции с данными, в том числеFile,Blob,ArrayBufferа такжеArray. Выбранный метод определяет внутреннюю структуру буфера в памяти.

Buffer

BufferдаNode.jsПредоставленный объект, интерфейс не имеет. Он обычно используется вIO操作Например, при получении данных внешнего запроса область буфера, предназначенная для хранения двоичных данных, может быть создана с помощью связанного с буфером API для интеграции полученных внешних данных.Буфер похож на целочисленный массив, но он соответствуетV8Часть необработанной памяти за пределами кучи памяти.

Аррайбуффер, ArrayBufferView

ArrayBuffer

ArrayBufferвыражать固定长度Оригинальный буфер двоичных данных, его функция заключается в выделении непрерывной области памяти, в которой могут храниться данные, поэтому он намного быстрее, чем Array в JS, для операций доступа с высокой плотностью (таких как аудиоданные).ArrayBuffer существует Смысл в том, что он заранее записывается в память как источник данных, поэтому его длина фиксирована

Давайте в общих чертах рассмотрим функции ArrayBuffer:

Конструктор объекта ArrayBuffer выглядит следующим образом (length указывает длину ArrayBuffer)👇:

ArrayBuffer(length);

Разница между Array и ArrayBuffer👇:

Array ArrayBuffer
Может поставить номера, строки, логические значения, объекты и массивы и т. Д. Может хранить только двоичные данные, состоящие из 0 и 1
данные в куче Данные помещаются в стек, что ускоряет выборку данных
свободно увеличивать или уменьшать Только для чтения, фиксированный размер после инициализации, независимо от того, пуст буфер или нет, его можно записать только с помощью TypedArrays и Dataview

Атрибуты

Свойства объекта ArrayBuffer смотрите в следующей таблице👇:

Имя свойства описывать
byteLength Указывает размер ArrayBuffer

метод

  • slice: Есть два параметра 👉beginуказывает на начало,endУказывает конечную точку. Метод возвращает новый ArrayBuffer, содержимое которого является байтовой копией этого ArrayBuffer, от начала (включительно) до конца (исключительно).

ArrayBuffer нельзя манипулировать напрямую, но черезTypedArrayилиDataViewОни преобразуют данные в буфере в массивы данных различных типов и считывают и записывают содержимое буфера через эти форматы. 👇

ArrayBufferView

Поскольку объект ArrayBuffer не предоставляет никаких методов для прямого чтения и записи памяти, аArrayBufferViewОбъект фактически основан на объекте ArrayBuffer.视图, который указывает原始二进制数据Базовый блок обработки объекта ArrayBufferView для чтения содержимого объекта ArrayBuffer. TypedArrays и DataView являются экземплярами ArrayBufferView.

TypedArrays

типизированный массив (TypedArrays) — это новая концепция в JavaScript, предназначенная для доступа к необработанным двоичным данным.По сути, типизированные массивы такие же, как ArrayBuffers, за исключением того, что они имеют функции чтения и записи.

Типы типизированных массивов: 👇:

имя размер (в байтах) иллюстрировать
Int8Array 1 8-битное целое число со знаком
Uint8Array 1 8-битное целое число без знака
Int16Array 2 16-битное целое число со знаком
Uint16Array 2 16-битное целое число без знака
Int32Array 4 32-битное целое число со знаком
Uint32Array 4 32-битное целое число без знака
Float32Array 4 32-битное число с плавающей запятой
Float64Array 8 64-битная с плавающей запятой

Преобразование типа, как показано ниже:Вот несколько примеров кода, показывающих, как конвертировать:

// 创建一个8字节的ArrayBuffer  
var b = new ArrayBuffer(8);  
  
// 创建一个指向b的视图v1,采用Int32类型,开始于默认的字节索引0,直到缓冲区的末尾  
var v1 = new Int32Array(b);  // Int32Array(2) [0, 0]
v1[0] = 1
console.log(v1); // Int32Array(2) [1, 0]
  
// 创建一个指向b的视图v2,采用Uint8类型,开始于字节索引2,直到缓冲区的末尾  
var v2 = new Uint8Array(b, 2);  // Uint8Array(6) [0, 0, 0, 0, 0, 0]
  
// 创建一个指向b的视图v3,采用Int16类型,开始于字节索引2,长度为2  
var v3 = new Int16Array(b, 2, 2);  // Int16Array(2) [0, 0]

Поскольку обычные массивы Javascript используют метод поиска по хешу, а типизированные массивы напрямую обращаются к фиксированной памяти, скорость очень хорошая, быстрее, чем у традиционных массивов! В то же время типизированные массивы по своей природе обрабатывают двоичные данные, что полезно дляXMLHttpRequest,canvas,webGLи другие технологии имеют неотъемлемые преимущества.

TypedArray的应用如何拼接两个音频文件

fetch请求音频资源 -> ArrayBuffer -> TypedArray -> 拼接成一个 TypedArray -> ArrayBuffer -> Blob -> Object URL

DataView

DataViewОбъекты могут считывать и хранить различные типы двоичных данных в любом месте ArrayBuffer.

Синтаксис создания DataView следующий:

var dataView = new DataView(DataView(buffer, byteOffset[可选], byteLength[可选]);
Атрибуты

Объект DataView имеет три свойства, см. следующую таблицу👇:

Имя свойства описывать
buffer Представляет ArrayBuffer
byteOffset Относится к смещению от начала буфера
byteLength Относится к длине буферной секции
метод
  • setint8():отDataViewНачальная позиция хранит 8-битное число (один байт) с указанным смещением (byteOffset), считая в байтах.
  • getint8():отDataViewПолучить 8-битное число (один байт) по указанному смещению (byteOffset) в байтах от начальной позиции

Кроме того, есть getInt16, getUint16, getInt32, getUint32... Методы использования одинаковы, поэтому я не буду перечислять их здесь по одному.

Использование следующее👇:

let buffer = new ArrayBuffer(32);
let dataView = new DataView(buffer,0);
dataView.setInt16(1,56);
dataView.getInt16(1); // 56

FileReader

Мы не можем получить прямой доступ к содержимому объектов Blob или файлов. Если вы хотите прочитать их и преобразовать в другие форматы, вы можете использоватьFileReaderAPI объекта для работы

  • readAsText(Blob): преобразовать BLOB-объект в текстовую строку.
  • readAsArrayBuffer(Blob): преобразование данных формата Blob в ArrayBuffer.
  • readAsDataURL(): конвертировать Blob в DataURL в формате Base64.

Используйте следующие 👇:

    const blob = new Blob(['<xml>foo</xml>'], { type: 'text/xml' });
    console.log(blob); // Blob(14) {size: 14, type: "text/xml"}

    const reader = new FileReader();
    reader.onload = () => {
      console.log(reader.result);
    };
    reader.readAsText(blob); // <xml>foo</xml>
    reader.readAsArrayBuffer(blob); // ArrayBuffer(14) {}
    reader.readAsDataURL(blob); // data:text/xml;base64,PHhtbD5mb288L3htbD4

Попробуем прочитать содержимое файла в виде строки:

<input type="file" id='f' />
<script>
  document.getElementById('f').addEventListener('change', function (e) {
    var file = this.files[0];
    // 首先,需要创建一个FileReader的实例。
    const reader = new FileReader();
    reader.onload = function () {
        // 在加载完成时回调
        const content = reader.result;
        console.log(content);
    }
    reader.readAsText(file); // 将blob转化为文本字符串读取
  }, false);
</script>

Результаты чтения следующие👇:

BlobURL

BlobURL(ObjectURL) является伪协议, может быть сгенерирован только внутри браузера, мы знаемscript/img/video/iframeАтрибут src тега и URL-адрес фона могут отображаться с помощью URL-адреса и base64. Мы также можем преобразовать большой двоичный объект или файл в URL-адрес, чтобы сгенерировать BlobURL для отображения изображений. BlobURL позволяет использовать объекты Blob и File в качестве изображений, URL-адресов для загрузка бинарных данных по ссылкам и др. источнику.

Отображение изображения👇:

  <div id="content">
    <input type="file" multiple id="f" />
  </div>
  <script>
    const elem = document.getElementById('f');
    const content = document.getElementById('content');
    
    // 根据不同浏览器封装一个转换BlobUrl的方法:file可以是File对象也可以是Blob对象
    const getObjectURL = (file) => {
      let url;
      if (window.createObjectURL) {
        url = window.createObjectURL(file);
      } else if (window.URL) {
        url = window.URL.createObjectURL(file);
      } else if (window.webkitURL) {
        url = window.webkitURL.createObjectURL(file);
      }
      return url;
    };

    elem.onchange = function (event) {
      const files = event.target.files;
      const file = files[0];
      const img = document.createElement('img');
      img.src = getObjectURL(file);
      content.appendChild(img);
    };
  </script>

Когда мы посмотрим на элемент изображения mm на демонстрационной странице, мы обнаружим, что его URL-адрес не является ни традиционным HTTP-адресом, ни URL-адресом Base64, а строкой, начинающейся с blob:, которую можно проверить, поместив ее в адресную строку.

Загрузка файла👇:

<body>
 <button onclick="download()">download.txt</button>

 <script>
      const getObjectURL = (file) => {
        let url;
        if (window.createObjectURL) {
          url = window.createObjectURL(file);
        } else if (window.URL) {
          url = window.URL.createObjectURL(file);
        } else if (window.webkitURL) {
          url = window.webkitURL.createObjectURL(file);
        }
        return url;
      };
      function download() {
        const fileName = 'download.txt';
        const myBlob = new Blob(['johnYu'], { type: 'text/plain' });
        downloadFun(fileName, myBlob);
      }
      function downloadFun(fileName, blob) {
        const link = document.createElement('a');
        link.href = getObjectURL(blob);
        link.download = fileName;
        link.click();
        link.remove();
        URL.revokeObjectURL(link.href);
      }
    </script>
  </body>

Нажмите кнопку, чтобы загрузить документ, содержание документа:johnYu

сюда не звонилиrevokeObjectURLпосещениеchrome://blob-internals/Вы можете увидеть текущий список внутренних файлов больших двоичных объектов:BlobUrls, которые больше не используются, будут автоматически удалены позже (закрытие браузера также автоматически очистит), но лучше всего использоватьURL.revokeObjectURL(url)Очистите их вручную:

URL.revokeObjectURL('blob:http://127.0.0.1:5500/d2a9a812-0dbf-41c5-a96b-b6384d33f281');

вернуться после казниchrome://blob-internals/Вы можете видеть, что файл был очищен

dataURL

dataURLПозволяет создателям контента встраивать файлы меньшего размера в документы. Аналогично обычным вариантам использования URL

Его синтаксический формат выглядит следующим образом:

data:[<mediatype>][;base64],data
  • data: приставка
  • mediatypeУказывает тип данных, являетсяMIMEВведите строку, например image/jpeg для файла изображения JPEG. Если опущено, по умолчаниюtext/plain;charset=US-ASCII.
  • base64:flags (необязательно, если текст)
  • data: сами данные

Как получить DataUrl

  1. Метод readAsDataURL(), используемый в приведенном выше примере, предназначен для преобразования BLOB-объектов в DataUrl в формате Base64;
  2. использовать роднойWeb APIкодировать декодировать

В Javascript есть две функции, отвечающие за кодирование и декодирование строк base64: atob и btoa. Оба обрабатывают данные только в URL-адресе данных.

btoa('<xml>foo</xml>') // "PHhtbD5mb288L3htbD4="
atob('PHhtbD5mb288L3htbD4=') // "<xml>foo</xml>"
  • atob(): отвечает за декодирование строки в кодировке base64.
  • btoa(): преобразовать двоичную строку в кодировку base64.ASCIIнить.
  1. Метод toDataURL холста:

Canvas предоставляет метод toDataURL для получения содержимого рисунка холста и преобразования его в формат base64.

  <body>
    <canvas id="canvas" width="200" height="50"></canvas>
    <textarea id="content" style="width: 200px; height: 200px"></textarea>

    <script>
      var canvas = document.getElementById('canvas');
      if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
        // canvas的绘制
        ctx.font = 'Bold 20px Arial';
        ctx.textAlign = 'left';
        ctx.fillStyle = 'purple';
        ctx.fillText('johnYu', 10, 30);
        // 获取 Data URL
        document.getElementById('content').value = canvas.toDataURL();
      }
    </script>
  </body>

Как показано на рисунке ниже, содержимое текстового поля представляет собой формат base64 содержимого, отображаемого на холсте.

Если мы вернем результат предыдущегоdata:text/xml;base64,PHhtbD5mb288L3htbD4=Вставьте его в адресную строку вашего браузера, и вы увидите, что отображается.

Использование DataUrl

  1. Поскольку его можно использовать вместо URL-адреса, DataURL можно использовать в атрибуте src таких тегов, как script/img/video/iframe, и в фоновом URL-адресе, таком как BlobUrl. Использование в основном такое же, как и у BlobUrl.elem.onchangeСделайте следующее преобразование
<body>
    <div id="content">
      <input type="file" multiple id="f" />
    </div>
    <script>
      const elem = document.getElementById('f');
      const content = document.getElementById('content');

      elem.onchange = function (event) {
        const files = event.target.files;
        const file = files[0];
        const img = document.createElement('img');
-        img.src = getObjectURL(file);
+        const reader = new FileReader();
+        reader.onload = function () {
+          img.src = reader.result;
+        };
+        reader.readAsDataURL(file);
        content.appendChild(img);
      };
    </script>
  </body>
  1. Поскольку сами данные представлены URL-адресом, их можно сохранить в файле cookie и передать на сервер.
  2. Когда размер изображения слишком мал, использование HTTP-сессии не очень целесообразно.
  3. Когда доступ к внешним ресурсам затруднен или ограничен
  4. DataUrl не будет кешироваться браузером, но небольшая часть будет кешироваться css.В следующем примере использование DataUrl полностью соответствует сцене. Это позволяет избежать того, чтобы это маленькое фоновое изображение генерировало HTTP-запрос само по себе, и это маленькое изображение также может кэшироваться браузером вместе с файлом CSS и повторно использоваться вместо загрузки каждый раз, когда оно используется. Пока изображение не очень большое и не используется повторно в файле CSS, метод DataUrl может отображать изображение, чтобы сократить время загрузки страницы и улучшить работу пользователя при просмотре.
 background-image: url("data:image/gif;base64,R0lGODlhAwADAIAAAP///8zMzCH5BAAAAAAALAAAAAADAAMAAAIEBHIJBQA7"); 
  1. Использовать как ссылку для скачивания
  <script>
    const createDownload = (fileName, content) => {
      const blob = new Blob([content]);
      const reader = new FileReader();
      const link = document.createElement('a');
      link.innerHTML = fileName;
      link.download = fileName;
      reader.onload = () => {
        link.href = reader.result;
        document.getElementsByTagName('body')[0].appendChild(link);
      };
      reader.readAsDataURL(blob);
    };

    createDownload('download.txt', 'johnYu');
  </script>

Нажав на тег a, загрузите txt-файл с текстовым содержимым johnYu, то же самое можно сделать в следующем BlobURL👇


разница

Основное использование BlobURL такое же, как и у DataUrl, оба можно проверить, поместив его в адресную строку, а также можно использовать как обычный URL-адрес.

Однако имеются следующие отличия.

  1. BlobUrl всегда является уникальной строкой, даже если вы каждый раз передаете один и тот же Blob, каждый раз будет генерироваться другой BlobUrl; значение DataUrl меняется вместе с blob;
  2. Что касается BlobUrl, то он не представляет сами данные, данные хранятся в браузере, а BlobUrl — это просто ключ для доступа к ним. Данные будут оставаться действительными до тех пор, пока браузер не будет закрыт или очищен вручную. А DataUrl — это непосредственно закодированные данные. Таким образом, даже передача BlobUrl на сервер и т. д. не может получить доступ к данным. После закрытия браузера DataUrl по-прежнему доступен в адресной строке, но доступ к BlobUrl недоступен.
  3. Длина BlobUrl, как правило, относительно короткая, а DataUrl часто бывает очень длинной, потому что он непосредственно хранит данные изображения в кодировке base64 (объем данных в кодировке Base64 обычно на 1/3 больше, чем у изображения в двоичном формате. ), поэтому, когда большое изображение отображается явно, используйте BlobUrl для лучших возможностей, скорости и эффективности использования памяти, чем DataUrl
  4. BlobUrl может легко использовать источник получения данных XMLHttpRequest (xhr.responseType = 'blob'). Для DataUrl не все браузеры поддерживают получение исходных данных через XMLHttpRequest.
  <body>
    <button onclick="download1()">XMLHttpRequest下载</button>
    <button onclick="download2()">fetch下载</button>
    <img id="img" />
    <script>
      var eleAppend = document.getElementById('forAppend');
      const url = 'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/9ecb4e119c26e64b8b4ec5258f159b3b~300x300.image';
      const pingan = document.querySelector('#pingan');
      function download1() {
        const xhr = new XMLHttpRequest();
        xhr.open('get', url, true);
        xhr.responseType = 'blob';
        xhr.onload = function () {
          if (this.status == 200) {
            renderImage(this.response);
          }
        };
        xhr.send(null);
      }
      function download2() {
        fetch(url)
          .then((res) => {
            return res.blob();
          })
          .then((myBlob) => {
            renderImage(myBlob);
          });
      }

      function renderImage(blob) {
        window.URL = window.URL || window.webkitURL;
        var img = document.getElementById('img');
        img.onload = function (e) {
          window.URL.revokeObjectURL(img.src); // 清除释放
        };
        img.src = window.URL.createObjectURL(blob);
      }
    </script>
  </body>
  1. Помимо того, что BlobUrl может использоваться в качестве сетевого адреса ресурсов изображения, BlobUrl также может использоваться в качестве сетевого адреса других ресурсов, таких как файлы html, файлы json и т. д. Чтобы браузер мог правильно анализировать файл тип, возвращаемый BlobUrl, необходимо создать объект Blob, когда укажите соответствующий тип
    const createDownload = (fileName, content) => {
      const blob = new Blob([content], { type: 'text/html' });
      const link = document.createElement('a');
      link.innerHTML = fileName;
      link.download = fileName;
      link.href = getObjectURL(blob);
      document.getElementsByTagName('body')[0].appendChild(link);
    };
    createDownload('download.html', '<button>foo</button>');

6. DataUrl не кэшируется браузером, а значит загружается каждый раз при посещении такой страницы. Это проблема эффективности использования, особенно когда изображение активно используется на всем сайте. Но небольшая часть может быть кэширована css

canvas

CanvasЭлементы объекта отвечают за установку области на странице, после чего вы можете динамически рисовать графику в этой области через JavaScript.

метод

  • toDataURL(type, encoderOptions)): возвращает DataUrl в указанном формате, этот метод принимает два необязательных параметра.
    • тип: указывает формат изображения, по умолчанию — image/png
    • encoderOptions: Указывает качество изображения.Если задан формат изображения image/jpeg или image/webp, качество изображения можно выбрать от 0 до 1. Если он превышает диапазон значений, будет использоваться значение по умолчанию 0,92, а другие параметры будут игнорироваться.
  • toBlob(callback, type, encoderOptions): создание объекта Blob для отображения изображений на холсте. Тип изображения по умолчанию — image/png, а разрешение —96dpi
    • обратный вызов: параметр является функцией обратного вызова объекта blob.
  • getImageData(x,y,width,height): возвращает объект ImageData, который копирует пиксельные данные указанного прямоугольника холста.
    • x: Координата x верхнего левого положения для начала копирования.
    • y: Y-координата положения верхнего левого угла для начала копирования.
    • ширина: ширина прямоугольной области, которая будет скопирована.
    • высота: высота прямоугольной области, которая будет скопирована.
  • putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight): поместите данные изображения (из указанного объекта ImageData) обратно на холст.
    • imgData: указывает объект ImageData, который нужно вернуть на холст.
    • x: координата x верхнего левого угла объекта ImageData в пикселях.
    • y: координата y верхнего левого угла объекта ImageData в пикселях.
    • грязныйX: необязательно. Горизонтальное значение (x) в пикселях, где разместить изображение на холсте.
    • Грязный: необязательно. Горизонтальное значение (Y), в пикселях, где разместить изображение на холсте.
    • грязная ширина: необязательно. Ширина изображения, нарисованного на используемом холсте.
    • грязная высота: необязательно. Высота, используемая для рисования изображения на холсте.

Сценарии применения

Когда нам нужно получить содержимое холста, мы можем использоватьtoDataURLа такжеtoBlobАтрибуты (могут использоваться для подписи, обрезки изображения, сжатия изображения и других сценариев),putImageData,getImageDataЕго можно использовать для оттенков серого или копирования изображений (см. главу о сценариях использования ниже 👇)

Получить контент:

<body>
    <div id="content">
      <button onclick="drawnImg()">绘制图像</button>
      <button onclick="getImg()">获取图像</button>
      <canvas style="border: 1px solid black" id="drawing" width="200" height="200">A drawing of something.</canvas>
      <img src="./timg.jpg" alt="" />
    </div>
    <script>
      var drawing = document.getElementById('drawing');
      var quality = 0.3;
      const imgType = 'image/jpeg';

      var drawnImg = function () {
        if (drawing.getContext) {
          var context = drawing.getContext('2d');
          //取得图像的数据 URI
          var image = document.images[0];
          context.drawImage(image, 20, 20, 100, 100);
        }
      };
      var getImg = async function () {
        const content = getContent('base64');
        console.log(content);
        const content1 = await getContent('file');
        console.log(content1);
      };
      var getContent = function (type) {
        switch (type) {
          case 'base64':
            {
              const imgURL = drawing.toDataURL(imgType, quality);
              return imgURL;
            }
            break;
          case 'file':
            {
              // 转为文件格式
              return new Promise((resolve) => {
                drawing.toBlob(
                  (blob) => {
                    resolve(blob);
                  },
                  imgType,
                  quality
                );
              });
            }
            break;
        }
      };
    </script>
  </body>

Отношения и трансформации

строка → Uint8Array

    var str = 'ab';
    console.log(Uint8Array.from(str.split(''), (e) => e.charCodeAt(0))); // Uint8Array(2) [97, 98]

Uint8Array → строка

    var u8 = Uint8Array.of(97, 98);
    console.log(Array.from(u8, (e) => String.fromCharCode(e)).join('')); // ab

строка → URL-адрес данных

    var str = 'ab';
    console.log('data:application/octet-stream;base64,' + btoa(str)); // data:application/octet-stream;base64,YWI=

URL-адрес данных -> строка

    var data = 'data:application/octet-stream;base64,YWI=';
    console.log(atob(data.split(',')[1])); // ab

Uint8Array -> ArrayBuffer

    var u8 = Uint8Array.of(1, 2);
    console.log(u8.buffer); // ArrayBuffer(2) {}

ArrayBuffer -> Uint8Array

    var buffer = new ArrayBuffer(2);
    console.log(new Uint8Array(buffer)); // Uint8Array(2) [0, 0]

ArrayBuffer -> DataView

    var buffer = new ArrayBuffer(2);
    var dataView = new DataView(buffer, 0); // DataView(2) {}

DataView -> ArrayBuffer

    console.log(dataView.buffer); // ArrayBuffer(2) {}

ArrayBuffer → Большой двоичный объект

    var buffer = new ArrayBuffer(32);
    var blob = new Blob([buffer]);  // Blob {size: 32, type: ""}

UintXXArray → Большой двоичный объект

    var u8 = Uint8Array.of(97, 32, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33);
    var blob = new Blob([u8]);

строка → капля

    var blob = new Blob(['Hello World!'], {type: 'text/plain'}); // Blob {size: 12, type: "text/plain"}

Вышеупомянутое использование new Blob() для преобразования blob

DataUrl -> blob

    var data = 'data:application/octet-stream;base64,YWI=';
    function dataURLtoBlob(dataurl) {
      var arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);

      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], { type: mime });
    }
    console.log(dataURLtoBlob(data)); // Blob {size: 2, type: "application/octet-stream"}

Клякса →

Необходимо использовать API FileReader для преобразования readAsText (Blob), readAsArrayBuffer (Blob), readAsDataURL(), но его необходимо выполнять асинхронно.

    var blob = new Blob(['a Hello world!'], { type: 'text/plain' });
    var reader = new FileReader();
    reader.readAsText(blob, 'utf-8');
    reader.onload = function (e) {
      console.info(reader.result); // a Hello world!
    };
    reader.onerror = function (e) {
      console.error(reader.error);
    };

Вы можете выполнять несколько преобразований с обещаниями

    var blob = new Blob(['a Hello world!'], { type: 'text/plain' });
    function read(blob) {
      var fr = new FileReader();
      var pr = new Promise((resolve, reject) => {
        fr.onload = (eve) => {
          resolve(fr.result);
        };
        fr.onerror = (eve) => {
          reject(fr.error);
        };
      });

      return {
        arrayBuffer() {
          fr.readAsArrayBuffer(blob);
          return pr;
        },
        binaryString() {
          fr.readAsBinaryString(blob);
          return pr;
        },
        dataURL() {
          fr.readAsDataURL(blob);
          return pr;
        },
        text() {
          fr.readAsText(blob);
          return pr;
        },
      };
    }
    var pstr1 = read(blob).binaryString();
    var pstr2 = read(blob)
      .arrayBuffer()
      .then((e) => Array.from(new Uint8Array(e), (e) => String.fromCharCode(e)).join(''));
    Promise.all([pstr1, pstr2]).then((e) => {
      console.log(e[0]); // a Hello world!
      console.log(e[0] === e[1]); // true
    });

Сценарии применения

изображение в градациях серого

В основном используется здесьcanvasа такжеimageDataпреобразование

<body>
    <button onclick="drawngray()">黑白图片</button>
    <img src="./syz.jpg" alt="" />
    <canvas id="myCanvas">canvas</canvas>
    <script>
      var drawngray = function () {
        var myCanvas = document.getElementById('myCanvas');
        if (myCanvas.getContext) {
          var context = myCanvas.getContext('2d');
          var image = document.images[0];
          // 动态设置canvas的大小
          myCanvas.width = image.width;
          myCanvas.height = image.height;
          var imageData, data, i, len, average, red, green, blue, alpha;
          //绘制原始图像
          context.drawImage(image, 0, 0);
          //取得图像数据
          imageData = context.getImageData(0, 0, image.width, image.height);
          data = imageData.data;
          for (i = 0, len = data.length; i < len; i += 4) {
            red = data[i];
            green = data[i + 1];
            blue = data[i + 2];
            // alpha = data[i + 3];
            //求得 rgb 平均值
            average = Math.floor((red + green + blue) / 3);
            //设置颜色值,透明度不变
            data[i] = average;
            data[i + 1] = average;
            data[i + 2] = average;
          }

          //回写图像数据并显示结果
          imageData.data = data;
          context.putImageData(imageData, 0, 0);
        }
      };
    </script>
  </body>

除次之外getImageData和putImageData还可以用作cavas图片复制:https://www.w3school.com.cn/tiy/t.asp?f=html5_canvas_getimagedata

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

Чтобы добиться сжатия изображения во внешнем интерфейсе, мы можем использовать метод toDataURL(), предоставляемый объектом Canvas.

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);
      // 通过toDataURL压缩后的base64
      let imageData = canvas.toDataURL(mimeType, quality / 100);
      resolve(imageData);
    };
  });
}

test.html

  <body>
    <input type="file" accept="image/*" onchange="loadFile(event)" />
    <script src="./compress.js"></script>
    <script>
      function dataUrlToBlob(base64) {
        var arr = base64.split(','),
          mime = arr[0].match(/:(.*?);/)[1],
          bstr = atob(arr[1]),
          n = bstr.length,
          u8arr = new Uint8Array(n);

        while (n--) {
          u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], { type: mime });
      }

      function uploadFile(url, blob) {
        let formData = new FormData();
        let request = new XMLHttpRequest();
        // 封装到FormData中进行文件的上传
        formData.append('image', blob);
        request.open('POST', url, true);
        request.send(formData);
      }

      const loadFile = function (event) {
        const reader = new FileReader();
        reader.onload = async function () {
          let compressedDataURL = await compress(reader.result, 90, 'image/jpeg');
          // 压缩后将base64转为Blob 对象减少传输数据量
          let compressedImageBlob = dataUrlToBlob(compressedDataURL);
          uploadFile('https://httpbin.org/post', compressedImageBlob);
        };
        // 获取用户选取的图片文件,通过FileReader转化成base64
        reader.readAsDataURL(event.target.files[0]);
      };
    </script>
  </body>

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

<body>
    <input type="file" name="file" onchange="selfile();" />

    <script>
      const url = 'https://httpbin.org/post';
      /**
       * @param file 原始文件
       * @param chunkSize 默认每次上传分片大小
       */
      async function chunkedUpload(file, chunkSize = 1024 * 1024 * 5) {
        // 将文件拆分成chunkSize大小的分块,然后每次请求只需要上传这一个部分的分块即可
        for (let start = 0; start < file.size; start += chunkSize) {
          // File对象继承自Blob对象,因此可以使用slice方法对大文件进行切
          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())
            .then((res) => console.log(res)); // 打印上传结果
        }
      }

      function selfile() {
        let file = document.querySelector('[name=file]').files[0];

        // 自定义分片大小
        const LENGTH = 1024 * 1024 * 1;
        chunkedUpload(file, LENGTH);
      }
    </script>
  </body>

После того, как сервер получит эти слайсы, их можно склеить вместе.Ниже приведен пример кода сплайсинга PHP-слайсов:

$filename = './upload/' . $_POST['filename'];//确定上传的文件名
//第一次上传时没有文件,就创建文件,此后上传只需要把数据追加到此文件中
if(!file_exists($filename)){
    move_uploaded_file($_FILES['file']['tmp_name'],$filename);
}else{
    file_put_contents($filename,file_get_contents($_FILES['file']['tmp_name']),FILE_APPEND);
    echo $filename;
}

Не забудьте изменить конфигурацию сервера nginx при тестировании, иначе большой файл может вызвать ошибку 413 Request Entity Too Large.

server {
	// ...
	client_max_body_size 50m;
}

Справочная статья 📜

❤️Понимание типов данных DOMString, Document, FormData, Blob, File, ArrayBuffer.

❤️Расскажите о бинарном семействе JS: Blob, ArrayBuffer и Buffer.

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

расширение 🏆

Если эта статья окажется для вас полезной, вы можете ознакомиться с другими моими статьями ❤️:

👍Боевые заметки vue3 | Быстрый старт 🚀

👍10 простых приемов, которые сделают ваш код vue.js более элегантным 🍊

👍Контакт с нулевым расстоянием с веб-сокетом 🚀

👍5 шаблонов проектирования, которые вы должны знать о веб-разработке

👍Структуры данных, которые должны знать веб-разработчики

👍Как получить размер экрана, окна и веб-страницы в JavaScript