На платформе облачной алгоритма Huawei нам нужно обработать большое количество файлов различных типов изображений. Мы знаем, что в повседневной работе обработки изображений это на самом деле относительно сложный процесс, который включает в себя следующие процессы: выбор локального предварительного просмотра файла, предварительный просмотр изображения загружается из сети, сжатие изображений и другая обработка, сервер загрузки изображения и т. Д. Этот процесс перемежается с морфологической обработкой файлов изображений, таких как: двоичный, BLOB, BASE64, ArrayBuffer и другие связанные знания. Далее мы возьмем полный процесс обработки изображений в качестве точки входа, чтобы систематически понять его необходимое содержание.
1. Как реализовать предпросмотр картинки
На самом деле есть два сценария реализации предварительного просмотра изображения:
- Загрузка локального изображения -> предварительный просмотр изображения
- Скачать картинки из сети --> Предварительный просмотр картинки
1.1 Как реализовать предварительный просмотр изображения через локальную загрузку
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片预览</title>
</head>
<body>
<h3>镇哥@吃橙子实例展示</h3>
<input type="file" onchange="loadFile(event)" />
<img id="previewContainer" />
</body>
<script>
function loadFile (event) {
const reader = new FileReader();
reader.onload = function () {
const output = document.querySelector("#previewContainer");
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
};
</script>
</html>
К типу входного файла входной коробки обработчика события ONCHANGE, в этой функции мы создали объект FILEREADER и связываете соответствующий обработчик событий Onload для объекта, а затем вызовов readAsdataUARL () метод fileheader объекта, локальный файл изображения, соответствующий Конвертировать URL-адрес данных. Когда чтение файла будет завершено, обработчик событий onload приведет к получению элемента данных URL-адреса данных, назначенный обработчик IMG SRC атрибута в интерьере, чтобы добиться локального предварительного просмотра изображения.
Для обоих методов ATOB и BTOA A A представляет ASCII, а B представляет собой BLOB, т.е. двоичный. Следовательно, ATOB представляет собой ASCII к двоичным, соответствующий операциям декодирования. BTOA представляет собой двоичный к ASCII, соответствующая операция кодирования.
const name = 'kobe bryant';
const encodedName = btoa(name);
console.log(encodedName); // a29iZSBicnlhbnQ=
const encodedName = 'a29iZSBicnlhbnQ=';
const name = atob(encodedName);
console.log(name); // kobe bryant
1.1 Как загружать изображения по сети для предварительного просмотра изображений
Мы можем использовать API выборки (developer.Mozilla.org/this-cn/docs/…Получить изображение из сети, а затем во время предварительного просмотра изображения:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片预览</title>
</head>
<body>
<h3>镇哥@吃橙子实例展示</h3>
<img id="previewContainer" style="width: 50%;" />
</body>
<script>
const image = document.querySelector("#previewContainer");
fetch("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F8138b202eb5c505b4e46159b2642477205c6b42c14bb5-FWbB38_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1628928976&t=5e9f3b7f70c32cc17f87500920124a75")
.then((response) => response.blob())
.then((blob) => {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
});
</script>
</html>
Мы получаем ресурс аватара с удаленного пути через API выборки.При успешном запросе мы преобразуем объект ответа (Response) в объект Blob, а затем используем метод URL.createObjectURL для создания URL-адреса объекта и присваиваем его атрибут src элемента img, таким образом реализовать отображение картинок.Внутри браузер хранит карту URL → Blob для каждого URL-адреса, созданного с помощью URL.createObjectURL, сам большой двоичный объект все еще находится в памяти, и браузер не может его освободить. Сгенерированный URL-адрес действителен только в том случае, если текущий документ открыт. Но если вы посетите URL-адрес большого двоичного объекта, который больше не существует, вы получите ошибку 404 в своем браузере. Таким образом, нам действительно нужно получить то, что должно быть объектом Blob.
1.1 Blob
Blob (Binary Large Object) представляет собой большой объект двоичного типа. В системе управления базами данных двоичные данные хранятся как набор одного объекта. Блобы обычно представляют собой изображения, звуковые или мультимедийные файлы. Чтобы более интуитивно чувствовать объект Blob, давайте сначала воспользуемся конструктором Blob для создания объекта myBlob, как показано на следующем рисунке:Синтаксис конструктора Blob:
const aBlob = new Blob(blobParts, options);
// blobParts:它是一个由 ArrayBuffer,ArrayBufferView,Blob,DOMString 等对象构成的数组。DOMStrings 会被编码为 UTF-8。
// options:一个可选的对象,包含以下两个属性:type —— 默认值为 ""; endings —— 默认值为 "transparent",用于指定包含行结束符 \n 的字符串如何被写入
// 实例代码:从字符串创建Blob
let myBlobParts = ['<html><h2>Hello Semlinker</h2></html>'];
let myBlob = new Blob(myBlobParts, {type : 'text/html', endings: "transparent"});
console.log(myBlob.size);
// Output: 37
console.log(myBlob.type);
// Output: text/html
Методы BLOB-объектов:
- slice([start[ end[ contentType]]]): возвращает новый объект Blob, содержащий указанный диапазон данных в исходном объекте Blob.
- Stream(): возвращает ReadableStream, который может читать содержимое BLOB.
- text(): возвращает объект Promise, содержащий UTF-8 USVString всего содержимого большого двоичного объекта.
- arrayBuffer(): возвращает объект Promise, содержащий ArrayBuffer в двоичном формате, содержащий все содержимое большого двоичного объекта.
Объекты Blob неизменяемы. Мы не можем изменять данные непосредственно в большом двоичном объекте, но мы можем разделить большой двоичный объект, создать из него новые объекты большого двоичного объекта и объединить их в новый большой двоичный объект. Это поведение похоже на строки JavaScript: мы не можем изменить символы в строке, но мы можем создать новую исправленную строку.
1.2 ArrayBuffer
Для объекта Response API выборки, в дополнение к методу blob(), объект также предоставляет такие методы, как json(), text(), formData() и arrayBuffer() для преобразования ответа в другой формат данных. Для предыдущего примера мы преобразуем объект ответа в объект ArrayBuffer, и изображения, загруженные из сети, также могут отображаться нормально Конкретный код выглядит следующим образом:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片预览</title>
</head>
<body>
<h3>镇哥@吃橙子实例展示</h3>
<img id="previewContainer" style="width: 50%;" />
</body>
<script>
const image = document.querySelector("#previewContainer");
fetch(
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F8138b202eb5c505b4e46159b2642477205c6b42c14bb5-FWbB38_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1628928976&t=5e9f3b7f70c32cc17f87500920124a75")
.then((response) => response.arrayBuffer())
.then((buffer) => {
const blob = new Blob([buffer]);
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
});
</script>
</html>
Объекты ArrayBuffer используются для представления универсальных буферов фиксированной длины необработанных двоичных данных. С ArrayBuffer нельзя манипулировать напрямую, его необходимо манипулировать через объекты типизированного массива или объекты DataView. Для некоторых часто используемых веб-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); // 可以将File 对象读取为 ArrayBuffer 对象
1.2 Разница между ArrayBuffer и Blob
- Объекты ArrayBuffer, используемые для представления исходного буфера двоичных данных общей фиксированной длины. Вы не можете напрямую манипулировать содержимым ArrayBuffer, но вам необходимо создать объект типизированного массива или объект DataView, представляющий буферы в определенном формате, и использовать этот объект для чтения и записи содержимого буфера.
- Объекты типа Blob представляют собой неизменяемые файловые объекты необработанных данных. Большие двоичные объекты не обязательно представляют данные в собственном формате JavaScript. Интерфейс File основан на Blob, унаследовав функциональность Blob и расширив ее для поддержки файлов в системе пользователя.
- Объекты Blob и ArrayBuffer можно преобразовать друг в друга:
- Используйте метод fileheader readasararaybuffer () для преобразования объекта BLOB на объект ArrayBuffer;
- Blob с помощью конструктора, поскольку новый объект Blob ([new Uint8Array (data]); объект ArrayBuffer может преобразовать объект Blob.
// Blob 转换为 ArrayBuffer
let blob = new Blob(["\x01\x02\x03\x04"]);
let fileReader = new FileReader();
let arrayRes;
fileReader.onload = function() {
arrayRes = this.result;
console.log("Array contains", array.byteLength, "bytes.");
};
fileReader.readAsArrayBuffer(blob);
// ArrayBuffer 转 Blob
let array = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
let blob = new Blob([array]);
2 Как бороться с эффектом картины холста
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas绘图</title>
</head>
<body>
<div>
<h3>灰度化处理</h3>
<div style="display: flex;">
<div style="flex: 50%;">
<p>预览容器</p>
<img id="previewContainer" src="./img/test.PNG" 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>
</body>
<script>
window.onload = function () {
// 绘制 canvas 图片:
drawCanvas();
}
function drawCanvas() {
const image = document.querySelector('#previewContainer');
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, 230, 230); // 绘制
// 取canvas数据重新绘制灰度化图片:
// 注意当用到getImageData方法获取图片信息时,会碰到跨域无法获取的情况,浏览器跨域 cros 设置或者将图片放在服务器中
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 10;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
ctx.putImageData(imageData, 0, 0);
}
</script>
</html>
Пиксели изображения, полученные методом ctx.getImageData(), отображаются в оттенках серого, и после завершения обработки обработанные пиксельные данные обновляются на Canvas методом ctx.putImageData().
3 Как добиться сжатия изображения через холст
В некоторых случаях мы надеемся, что при загрузке локального изображения изображение сначала сжимается до определенной степени, а затем отправляется на сервер, тем самым уменьшая объем передаваемых данных. Чтобы добиться сжатия изображения во внешнем интерфейсе, мы можем использовать метод toDataURL(), предоставленный объектом Canvas, который получает два необязательных параметра, type и encoderOptions:
- Тип представляет собой формат изображения, по умолчанию используется изображение / png;
- Параметр encoderOptions используется для указания качества изображения. Если задан формат изображения image/jpeg или image/webp, качество изображения можно выбрать от 0 до 1. Если он превышает диапазон значений, будет использоваться значение по умолчанию 0,92, а другие параметры будут игнорироваться.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas绘图</title>
</head>
<body>
<div>
<button id="compressbtn">图片压缩</button>
<div style="display: flex;">
<div style="flex: 33.3%;">
<p>预览容器</p>
<img id="previewContainer" src="./img/test.PNG" 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>
</div>
</body>
<script>
window.onload = function () {
// 绘制 canvas 图片:
drawCanvas();
// 图片压缩添加事件处理:
const compressbtn = document.querySelector("#compressbtn");
const compressImage = document.querySelector("#compressPrevContainer");
compressbtn.addEventListener("click", compress);
function compress(quality = 10, mimeType = "image/webp") {
const imageDataURL = canvas.toDataURL(mimeType, quality / 100);
compressImage.src = imageDataURL;
}
}
function drawCanvas() {
const image = document.querySelector('#previewContainer');
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, 230, 230); // 绘制
// 取canvas数据重新绘制灰度化图片:
// 注意当用到getImageData方法获取图片信息时,会碰到跨域无法获取的情况,浏览器跨域 cros 设置或者将图片放在服务器中
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 10;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
ctx.putImageData(imageData, 0, 0);
}
</script>
</html>
4 Как интерфейс обрабатывает загрузку изображений
Возвращаемые данные изображения в формате Data Url (то есть данные в кодировке base64) обычно относительно велики. Чтобы еще больше уменьшить объем передаваемых данных, мы можем преобразовать их в объект Blob:
function dataUrlToBlob(base64, mimeType) {
let bytes = window.atob(base64.split(",")[1]); // 解码 base64 数据
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);
}
5 Как фоновые службы читают и обрабатывают изображения
Давайте посмотрим, как сервер Node.js обрабатывает полученные данные URL-адреса данных (то есть данные в кодировке base64):
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("图片上传成功!");
}
});
});
6 Как добиться крупных интерфейсных кусков файлов Загрузить и загрузить файлы
Файловые объекты представляют собой особый тип BLOB-объектов и могут использоваться в контексте любого типа BLOB-объектов. Таким образом, для сценария передачи большого файла мы можем использовать метод среза, чтобы разрезать большой файл, а затем загрузить его по частям. Конкретный пример выглядит следующим образом:
// file 为 File 对象的一个实例
// url 是请求的url
const chunkSize = 40000;
async function chunkedUpload(url, file) {
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()
);
}
}
Мы создаем объект Blob типа text/plain, вызывая конструктор Blob, а затем загружаем файл, динамически создавая тег:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件下载实例</title>
</head>
<body>
<button id="downloadBtn">文件下载</button>
<script>
const downloadBtn = document.querySelector("#downloadBtn");
downloadBtn.addEventListener("click", (event) => {
const fileName = "blob.txt";
const myBlob = new Blob(["这是一个测试文件下载的的实例"], {
type: "text/plain"
});
download(fileName, myBlob);
});
function download(fileName, blob){
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = fileName;
link.click();
link.remove();
URL.revokeObjectURL(link.href);
};
</script>
</body>
</html>