Обработка изображений ReactJS — NodeJS

от автора

Доброго времени суток.

Разбор полетов провожу на Reactjs (сторона клиента) и Nodejs (сторона сервера).

Недавно в моем маленьком проекте встал вопрос, как легко и просто можно обмениваться изображениями по типу клиент — сервер.

Сегодня мы научимся отправлять бинарные данные (конкретно изображения ) со стороны клиента и обрабатывать их на сервере. Добро пожаловать в под кат.

Если ваше web-приложение — это соц.сеть или мессенджер или что-то подобное, то вам непременно придется работать с бинарными данными, в большинстве случаев — это обработка изображений. Об этом и пойдет речь.

Чтобы иметь общее представление, я буду толковать последовательно.

Ознакомимся со следующим

  1. Двоичная система счисления и десятичная система счисления
  2. Кодовая страница: ASCII, Unicode
  3. Бит и байт
  4. Формат hex
  5. Двоичный файл
  6. Буффер и потоки nodejs
  7. ArrayBuffer и Uint8Array
  8. FileReader

Двоичная система счисления и десятичная система счисления.

Это число, которое записывается с помощью двух символов: единиц и нулей.
Простым языком — это 2 (основание) в степени n (количество разрядов), например число 10001 имеет 5 разрядов:

4 разряд = 1; 2^4 = 16;
3 разряд = 0;
2 разряд = 0;
1 разряд = 0;
0 разряд = 1; 2^0 = 1;

1-true — производим расчет;
0-false — не производим расчет;

В итоге получаем 16 + 1 = 17;
Обратной операцией компьютер представляет данные в двоичном виде.

const num = 17; // десятичное число const binNum = num.toString(2); // преобразование в двоичный вид const initNum = parseInt(binNum, 2); // обратная операция 

Десятичная система счисления — это число, которое записывается с помощью 10 символов, от 0 — 9. Мы используем их повседневно.

Дело в том, что компьютер живет своим представлением информации. Если мы пишем на родном языке или считаем в десятичной системе счисления, то для компьютера все это двоичное представление. Вы спросите: “Что значит двоичное представление?”. Как говорила моя учительница английского, “как слышится, так и пишется”. Компьютер представляет файлы, содержащие текст или иное, или какие-либо вычисления в двоичную систему счисления — это и есть двоичное представление. Языком машины число «17» является — 10001.

Кодовая страница: ASCII, Unicode

Возникает вопрос. Понятно с числами, но как же текст? Как машина будет преобразовывать текст в двоичный вид? Все просто. Для этого существуют кодировки типа ASCII или Unicode. Это простая таблица ( кодовая страница ), где имеется сопоставление нашему символу число, например символ “S” — это число 53 по таблице ASCII, далее компьютер представит число 53 в двоичном виде — 110101.
Что касаемо Unicode — это мощный стандарт, включающий в себя кодировку UTF-8, которая имеет большой спрос в web-пространстве и unix подобных системах. Принцип работы общий.

Бит и байт

Бит — это единица измерения информации компьютером. А мы уже знаем как компьютер представляет информацию. Да! Вы правы. Один бит равен 1 или 0. Для компьютера 1 или 0 — это что-то вроде транзистора, true или false. Теперь понять что такое байт совсем не трудно — это 8 битов, его еще называют октет ( типа состоит из 8 ).

Формат hex

Переходим к “hex” формату. Мы говорили о двоичной системе счисления, десятичной системе, помимо прочего существует и шестнадцатиричная система счисления, верстальщики поймут о чем я. Это число, которое записывается с помощью 16 символов, от 0 — 9 и от a-f. Навивается вопрос: “Зачем так все усложнять?”. Так как единицей памяти является 8-битный байт, все же значение его удобнее записывать двумя шестнадцатиричными цифрами. Также в стандарте Юникода номер символа принято записывать в шестнадцатиричном виде, используя не менее 4 цифр.
Данный формат работает следующим образом: в случае с 8 битным байтом, он делит 8 битов по полам, то есть 10101011 — 1010 | 1011, после преобразует каждую часть соответственно. 1010 — a, 1011 — b;

const num = 196; // десятичное число const hexNum = num.toString(16); // преобразование в hex вид const initNum = parseInt(hexNum, 16); // преобразование в десятичный вид - обратная операция 

Двоичный файл

Переходим к следующему пункту, его еще называют бинарным файлом — это массив байтов. Почему двоичный? Байты состоят из битов, то есть символов двоичной системы счисления, отсюда и название двоичный. Под категорию двоичный файл подходят абсолютно все файлы, то есть файлы содержащие текст, изображения — все это двоичные файлы, отсюда двоичные данные, отсюда бинарные данные.

Нередко бинарные данные выводятся символами кодовой страницы ASCII, а также в формате hex, в частности изображения. Приведу пример. У нас есть следующее изображение:

Смотреть вложение

Его частичная визуализация представляется в виде hex-agent-smith

Смотреть вложение


Правая часть символов является символами таблицы ASCII, левая же часть как раз и есть hex формат, преобразованный от двоичного кода.

ArrayBuffer и Uint8Array

ArrayBuffer — объект для работы с бинарными данными на javascript. Фиксирует длину памяти для работы с бинарником. Например: new ArrayBuffer(256) — создаст массивоподобный объект размером в 256 байт, не больше — не меньше. На каждый байт приходится конкретная информация. ArrayBuffer не является массивом и методы массива к нему не применимы. Вы зададите вопрос, как же его использовать? Объект Uint8Array дает представление ArrayBuffer в беззнаковое 8-битное число, то есть от 0 — 255.

FileReader

FileReader? Это конструктор по созданию следующего:

<input type="file">

Что дальше?

Теперь рассмотрим прекрасный пример, демонстрирующий обработку изображения:

  1. Модуль ReactJS
    import React, {useState, useRef, useCallback} from 'react'; import axios from 'axios';  export const UPLOAD_AVATAR = 'http://localhost:8080/api/upload_avatar';  function App() {   // определим изменяемый ref для объекта FileReader   const fileRef = useRef(null);   const [ loading, setLoading ] = useState(false);    const handleSubmit = useCallback( event => {     event.preventDefault();      const fetchData = async (uint8Array) => {       try {         const response = await axios({           method: 'post',           url: UPLOAD_AVATAR,           data: [...uint8Array] // не отправляем в JSON, размер изображения увеличится         });          setLoading(false);         console.log(response);       } catch (e) {         console.error(e.message, 'function handleSubmit')       }     };      if(!fileRef.current) return void null;      const reader = new FileReader();     reader.onloadend = () => {       const uint8Array = new Uint8Array(reader.result);       setLoading(true);       fetchData(uint8Array);     };       // рекомендованный метод     reader.readAsArrayBuffer(fileRef.current[0]);      // метод reader.readAsBinaryString(fileRef.current[0])      // согласно MDN,     // уже был однажды удален из File API specification,      // но после его вернули     // в использование, но все же рекомендуют      // использовать readAsArrayBuffer     // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString   }, []);    const nodeDom = (     <form onSubmit={handleSubmit}>       <div>         <input           onChange={e => fileRef.current = e.target.files}           accept="image/*"           type="file"           id="button-file"         />       </div>       <button type="submit">{loading ? 'Сохраняю...' : 'Сохранить'}</button>     </form>   );    return nodeDom }  export default App;                 

  2. Модуль ExpressJS
    const express = require("express"); const cors = require("cors");  const crypto = require('crypto'); const fs = require('fs');  const PORT = 8080;  const app = express();  app.use(express.static("public")); app.use(express.json({ limit: "50mb" })); app.use(cors());  app.post("/api/upload_avatar", (req, res) => {   console.log(req.body);   const randomString = crypto.randomBytes(5).toString('hex');   const stream = fs.createWriteStream(`./public/images/${randomString}.png`);    stream.on('finish', function () {     console.log('file has been written');     res.end('file has been written');   });    stream.write(Buffer.from(req.body), 'utf-8');   stream.end(); });  app.listen(PORT, () => console.log(`Server running at ${PORT}`));                

Итог:

  • Для чего нужны кодировки
  • Общее представление бинарных данных
  • Рекомендуемый способ отправки байтов
  • Объект буфера
  • Объект FileReader
  • Простая реализация на хуках

ссылка на оригинал статьи https://habr.com/ru/post/491548/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *