Версия 2.0
«Не разводите сырость»
Александр Сушко

Сырые данные — это...
Сырые данные — это данные,
которые не были проанализированы,
закодированы или преобразованы
Сырые данные, в нашем случае,—
это содержимое файлов
BLOB
Binary Large Object
Массив бинарных данных.
Иначе — двоичных
FileAPI
Определяет, как нам работать
с содержимым файлов
прямо в браузере
Пересказ
Предыстория...

<?xml version="1.0" encoding="windows-1251"?>
<Файл xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ИдФайл="ON_DOCNPNO_6321422260632101001_6321422260632101001_0093_20180704_f9123170cc014fe5bff51ccbb10f8aa6" ВерсПрог="Налог3-А" ВерсФорм="5.01" xsi:noNamespaceSchemaLocation="ON_DOCNPNO_1_886_00_05_01_01.xsd">
<Документ КНД="1184002" ДатаДок="28.12.2017">
<СвОтпрДок>
<ОтпрНО КодНО="0093" НаимНО="0093" />
</СвОтпрДок>
<СвПолДок>
<ОтпрЮЛ НаимОрг="ООО "ШУЛЕР СЕРВИС РУС"" ИННЮЛ="6321422260" КПП="632101001" />
</СвПолДок>
<СвНП>
<НПЮЛ НаимОрг="ООО "ШУЛЕР СЕРВИС РУС"" ИННЮЛ="6321422260" КПП="632101001" />
</СвНП>
<Подписант Должн="Руководитель">
<ФИО Фамилия="Reg" Имя="Ofd" Отчество="Test" />
</Подписант>
<ДокНапрИзНО ИдФайлЗаяв="KO_ZVLREGKKT_0093_0093_6321422260632101001_20180704_defd99c90e3e4035809c666366675770">
<ИнфСообДок КНД_Док="1110211" НомФайлДок="167897" ДатаФайлДок="28.12.2017" КолФайл="1">
<ИмяФайл>1110211_0093_6321422260632101001_2b4edfd4-d702-4a6e-91e4-20d06731e178_20180704_2b4edfd4-d702-4a6e-91e4-20d06731e178</ИмяФайл>
</ИнфСообДок>
<ИнфСообДок КНД_Док="1110211" НомФайлДок="167897" ДатаФайлДок="28.12.2017" КолФайл="1">
<ИмяФайл>1110211_0093_6321422260632101001_3a3dde30-6c64-43ff-b129-79b0c471dee3_20180704_3a3dde30-6c64-43ff-b129-79b0c471dee3</ИмяФайл>
</ИнфСообДок>
</ДокНапрИзНО>
</Документ>
</Файл>


Нюанс
Фронтенд
Бэкенд
«Принтер»
Фронтенд
Бэкенд
«Принтер»
Скачать документ
Подготавливаем документ
...
fetch("/api")
.then(function(response) {
return response.json();
})
.then(function(data) {
window.location.href = data.fileUrl;
});

// console.log(response);
{
type: "application/pdf",
size: 12345,
fileContent: "SGVsbG8sIHdvcmxkIQ..."
}

// console.log(response);
{
type: "application/pdf",
size: 12345,
fileContent: "SGVsbG8sIHdvcmxkIQ..."
}
public class Print
{
public Guid Id { get; set; }
public string Type { get; set; }
public int Size { get; set; }
public byte[] FileContent { get; set; }
}
Base64?

fetch("/api")
.then(function(response) {
return response.json();
})
.then(function(data) {
const { type, fileContent } = data;
window.location.href =
`data:${type};base64,${fileContent}`;
});


IE и Base64
Ограничение в 32,768 символов
«Только» картинки
Свои требования к кодированию спец.символов
–
–
–

async function downloadFile(filename) {
const { content, type } = await api.getFile();
const blob = new Blob([content], { type: type });
if (isMsBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
setTimeout(function() {
window.URL.revokeObjectURL(link.href);
document.body.removeChild(link);
}, 0);
}
}
const binString = window.atob(content);
const buffer =
new Uint8Array(binString.length);
for (let i = 0; i < binString.length; i++) {
buffer[i] = binString.charCodeAt(i);
}
const blob = new Blob([buffer], { type });
Ctrl + C Ctrl + V
async function download(filename) {
const { content, type } = await api.getFile();
const binString = window.atob(content);
const buffer = new Uint8Array(binString.length);
for (let i = 0; i < binString.length; i++) {
buffer[i] = binString.charCodeAt(i);
}
const blob = new Blob([buffer], { type: type });
if (isMsBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
setTimeout(function() {
window.URL.revokeObjectURL(link.href);
document.body.removeChild(link);
}, 0);
}
}

FileAPI
FileAPI это...
Определяет, как нам работать
с содержимым файлов
прямо в браузере
История
2006 – First Public Draft
2009 – Working Draft
2013 – Last Call
2017 – снова Working Draft
Поддержка
Firefox, Chrome, Safari
Edge 16+ (с оговоркой)
IE 10+ (с оговорками)
–
–
–
FileAPI
FileList и File
FileReader
Blob
–
–
–
Методы расширения window.URL
–
FileList и File
FileList
FileList – список File.
const files =
document.querySelector("[type=\"file\"]").files;
for (let file of files) { /*...*/ }
files.item(0);
Всё!
File
Blob с метаданными о файле.
{
lastModified,
lastModifiedDate,
name
}
File
Blob с метаданными о файле.
const file = new File(blob, filename, options);
Оговорка: IE и Edge не умеют

It's demo time!
blob:
blob:
Ссылка на Blob содержимое.
blob:https://example.org/9115d58c-bcda-ff47-86e5...
blob:
Ссылка на Blob содержимое.
/* Создание */
const link =
window.URL.createObjectURL( file || blob );
/* Удаление */
window.URL.revokeObjectURL(link);
blob:
Ссылка живёт в рамках document
Не хранит в себе данных
–
–

It's demo time!
FileReader
FileReader
Позволяет читать содержимое файлов пользователя.
const filereader = new FileReader();
filereader.readAsText( file || blob );
FileReader
Умеет в асинхронность
Можно прервать чтение
–
–
Умеет показывать прогресс
–

It's demo time!
Blob
Blob
Объект, содержащий бинарные данные, сырые и неизменяемые.
const blob = new Blob(
[ blob || arrayBuffer || string ],
{ type, endings }
);
blob.slice( start, end, type );

It's demo time!
Blob и xhr, level 2
xhr.responseType = "blob";
xhr.addEventListener("load", function() {
html5player.srcObject = xhr.response;
});
Blob и fetch
fetch("/video-stream.php")
.then(function(response) {
return response.blob();
})
.then(function(blob) {
html5player.srcObject = blob;
});
Blob и Canvas
const canvas = document.querySelector("canvas");
canvas.toBlob(function(blob) {
const link = window.URL.createObjectURL(blob);
image.src = link;
image.addEventListener("load", function() {
window.URL.revokeObjectURL(link);
});
});

It's demo time!
Нюансы Blob
Размер Blob ограничен
IE умеет Blob, но по своему
–
–
Так причём здесь сырость?
async function download(filename) {
const response = await api.getFile();
const binString = window.atob(response.content);
const buffer = new Uint8Array(binString.length);
for (let i = 0; i < binString.length; i++) {
buffer[i] = binString.charCodeAt(i);
}
const blob = new Blob([buffer], { type: response.type });
if (isMsBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
setTimeout(function() {
window.URL.revokeObjectURL(link.href);
document.body.removeChild(link);
}, 0);
}
}
Фронтенд
Бэкенд
«Принтер»
Фронтенд
Бэкенд
«Принтер»
Хранилище
Сервер – бог,
браузер – лох

Александр Сушко
Фронтенд-разработчик, СКБ Контур
Вопросы?
Ссылки
TypedArray
wiki.amperka.ru/js:typedarray
2ality.com/2015/09/typed-arrays.html
Working With Files in JavaScript
blog.brew.com.hk/working-with-files-in-javascript
Bytes and Blobs
Секретный раздел
ArrayBuffer
Контейнер определенной длины для хранения двоичных данных
Массив в истинном его понимании
Не поддерживает чтение или запись
–
–
–
TypedArray
Массивоподобное представление буфера с бинарными данными.
Существует «на словах».
Способы отображения

Не разводите сырость 2.0
By Alexander Sushko
Не разводите сырость 2.0
Версия презентации для NskTechTalks по фронтенду, 23 августа 2018
- 1,278