Learning Notes about Image in Frontend
These days when I handle the image in the frontend, there are some concepts make me confusing. How can we handle the image when saving, uploading and transfering the files? So I want to write some notes about the learning processing here.
- What is Blob, Buffer, ArrayBuffer and Base64 format?
- How to convert them among one another?
- How can they be used with Image?
Concept
ArrayBuffer
ArrayBuffer
is an build-in object in Javascript that represents a generic, fixed-length binary data buffer. It can be used to hold raw binary data that can be accessed with types like Int8Array
, Unit8Arrray
, Float32Array
and etc. ArrayBuffer is normally used to work with binary data, such as binary files and sending files over network.
But you cannot manipulate the ArrayBuffer data directly, instead, you need a typed array object or DataView
object to read or write the content of the buffer.
1const buffer = new ArrayBuffer(8);
2const view = new DataView(buffer);
3view.setInt16(0, 42);
4view.setFloat32(2, Math.PI);
5console.log(new Uint8Array(buffer));
You can check the detail here
Blob
Blob is the abbreviation of Binary Large OBject
. From its name, we can see it's used to save the raw data in the format of binary. And it can also be converted ReadableStream and represent some unusual data like File
. It's similar to ArrayBuffer
. However, Blob
can represent the text and image files. A Blob
object can contain the content of data and a MIME type which indicates the type of file.
1const text = 'Hello, world!';
2const blob = new Blob([text], { type: 'text/plain' });
3console.log(blob); // output: Blob { size: 13, type: "text/plain" }
Buffer
Buffer
is another object in Node.js
that represents binary data. It's similar to ArrayBuffer
, but it's designed for Node.js and has some additional features.
Buffer is an object in Node.js that represents a binary data buffer. It is similar to ArrayBuffer, but it is designed for use in Node.js and has some additional functionality. For example, Buffer has methods for encoding and decoding strings, and for copying and slicing buffers.
Base64
Base64 is a binary-to-text encoding scheme to represent binary data in an ASCII string format with a fixed set of 64 characters, consisting of uppercase and lowercase letters, digits and two additional symbols, '+' and '/'. It's commonly used to encode binary data, like image, audio and video files so that they can be transmitted over network. However, the problem of base64 is it takes an extra 33% data as each block of 3 bytes is represented as 4 characters in Javascript.
Conversion
So let's look at how to convert these format with one another.
Blob <-> ArrayBuffer
Blob can be converted to ArrayBuffer
with its static method .arrayBuffer
:
1const af = await blob.arrayBuffer()
Another way to read Blob
is to use a Response
. You can read the Blob
through the following code:
1const af = await new Response(blob).arrayBuffer()
Blob <-> base64
To convert the Blob to Base64 string, we need to use the FileReader
's method readAsDataURL
:
1const convertBase64 = (file: Blob) =>
2 new Promise((resolve, reject) => {
3 const fileReader = new FileReader();
4 fileReader.readAsDataURL(file);
5 fileReader.onload = () => {
6 resolve(fileReader.result);
7 };
8 fileReader.onerror = (error) => {
9 reject(error);
10 };
11 });
12
13const base64 = await convertBase64(blob)
To convert the base64 string to Blob, we need to check the format of the base64 string if it's a file, especially an image file. If it is, we need prepend the content type data.
1const base64: Response = await fetch(base64Data);
2// or
3const base64Response: Response = await fetch(`data:image/jpeg;base64,${base64Data}`);
And then obtain the Blob by Response
's method blob
:
1const blob = await base64Response.blob();
Blob <-> Buffer
Buffer is Nodejs platform based object, it's mostly used to transfer data. But sometimes we use Blob in the frontend and send to the backend, so we need to convert Blob to Buffer. We have two options to do this:
1const arrayBuffer = await blob.arrayBuffer();
2const buffer = Buffer.from(arrayBuffer);
Or
1const buffer = Buffer.from(blob, 'binary');
And sometimes, the data sent from frontend is not just a Blob file, it's a File
object, a special kind of Blob
.
1const data: Buffer = fs.readFileSync(imageFile.filepath);
Base64 <-> Buffer
Nodejs Buffer provides a very convenient method to convert from/to these two types:
1const base64 = Buffer.from(data).toString('base64');
2
3const buffer = Buffer.from(b64string, 'base64');
Image
How are these formats applied when we transfer, display and save the image?
From Url
With given url, we can display it directly in the html page by assign it to the src
of a img
element.
Blob Url
But what about the file uploaded?
Then we need to use Blob
url. Blob
URLs can only be generated internally by the browser. URL.createObjectURL()
will create a special reference to the Blob
or File
object which later can be released using URL.revokeObjectURL()
. These URLs can only be used locally in the single instance of the browser and in the same session.
1const imageBlob = fetch('https://example.com/image.png').then(response => response.blob());
2const imageUrl = URL.createObjectURL(imageBlob);
3const img = document.createElement('img');
4img.src = imageUrl; // blob:XXX...
If you need to pass the file object through different routes, like jump with link in the same page, besides save the file on the global place, like windonw
or localStorage, sessionStorage, we can also pass the file through url's query parameter
Communication with server
Send From Frontend
There are several ways to send and receive file from server:
HTML Form
: One common way to upload files to a server is using an HTML form with an input field of type file. When the form is submitted, the browser sends amultipart/form-data
request to the server, which can then process the uploaded file(s) as part of the request payload.
1<form id="uploadbanner" enctype="multipart/form-data" method="post" action="#">
2 <input id="fileupload" name="myfile" type="file" />
3 <input type="submit" value="submit" id="submit" />
4</form>
FormData
: You can also use theFetch API
orXMLHttpRequest
to send files to a server using FormData. The FormData object lets you build a set of key-value pairs that represent form fields and their values, including file inputs.
1const formData = new FormData();
2formData.append('imageFile', file, file.name);
3
4const response = await fetch(url, { method: 'POST', body: formData });
5const json = await response.json();
WebSocket
: If you need real-time file transfer, you can use WebSockets to establish a persistent connection between the client and server. You can then send file data through the WebSocket connection as a binary stream.
Receive By Backend
With browser supported FormData
object, we can use formidable
to handle it:
1import formidable from 'formidable';
2
3const post = async (req: NextApiRequest, res: NextApiResponse<Data>) => {
4 const fData = await new Promise<{ fields: any; files: any }>((resolve) => {
5 // extract the file with formidable
6 const form = new formidable.IncomingForm();
7 form.parse(req, (err, fields, files) => {
8 if (err) {
9 res.status(500).send({ result: 'Upload failed' });
10 return;
11 }
12 resolve({ fields, files });
13 });
14 });
15
16 const imageFile = fData.files.imageFile;
17}
To read the file as Nodejs's Buffer
, we can use the above method