This is my images table in PostgreSQL:
CREATE TABLE IF NOT EXISTS images (
----------------------------------------------------
id UUID NOT NULL DEFAULT uuid_generate_v4(),
----------------------------------------------------
pic BYTEA NOT NULL,
ext VARCHAR(10) NOT NULL,
----------------------------------------------------
CONSTRAINT images_pk
PRIMARY KEY (id)
----------------------------------------------------
);
-- Inserting Fake Data
SELECT set_config(
'default_pic_path',
'/path/to/image.png',
FALSE
);
INSERT INTO
mi.images (
pic,
ext
)
VALUES
------------------------------
(
bytea('default_pic_path'),
'png'
);
------------------------------
And I would like to have an endpoint for adding images to the DB and also another for retrieving them, sending them back with the proper image extension (e.g. .png, .jpg, etc.).
What is the recommended way of doing this in ExpressJS with node-pg?
So far, I've found tons of ways of doing this converting images to text, but that's highly inefficient... I've also found some solutions in which we turn the returned node-pg Buffer into a local file and then use res.sendFile(...), but that is also highly inefficient. Is there a better way?
Anyways, here's a sketch of an endpoint for getting an image (in TypeScript), with the local creation of a file I had criticized:
import { Request, Response } from "express";
export async function getImage(req: Request, res: Response) {
try {
const mainPool = new Pool({
user: "postgres",
password: "password",
database: "main",
host: "localhost",
port: 5432,
});
const results = await mainPool.query(/* sql */ `
SELECT pic, ext
FROM images
`);
const pic = results.rows.[0].pic as Buffer;
const extension = results.rows.[0].ext as string;
const filename = `default_pic.${extension}`;
const filepath = join(
__dirname,
"../path/to/assets/folder/",
`assets/${filename}`
);
// Doesn't really write the file properly,
// probably some error with encoding...
writeFileSync(filepath, pic);
res.sendFile(filepath);
} catch (e) {
console.log(e);
}
}
Here's a small sample. It's Javascript instead of Typescript. But it will make it clear anyways.
It uses a slightly modified schema:
The main difference is that a content type instead of a file extension is used.
To insert images using SQL, the Postgres server needs to have access to the images. And you need to have the permissions to execute
pg_read_binary_file():The sample also assumes that a HTML file called
upload.htmlis in a directory calledpublic. It displays the UI for the file upload.upload.html
The node.js/express part is quite compact. It assumes the following npm packages are installed:
expresspgexpress-fileuploadTo view an image, go to
http://localhost:3000/img/cef95cc8-137a-4a06-9d0d-ccee0cb018be(replacing the UUID with the id of the image you want to view).To upload an image, go to
http://localhost:3000/upload.htmlThe sample makes use of the fact that express-fileupload makes the uploaded file available as a
Buffer. And the Postgres client accepts aBufferfor inserting the image data. It also returns aBufferwhen querying theimagecolumn. Andres.send()also accepts aBuffer.Since the
Bufferinstance are held in memory, this approach is efficient. But the maximum image size is limited by memory.Update
It the image should be uploaded using Javascript, minor modifications are needed. It still assumes that the image has been selected in a user interface using an input element of type
file.Additional server-side code
If the upload is successful, this endpoint returns JSON data instead of redirecting to the image URL.
upload2.html
If the image is uploaded successfully, a link appears with the image URL. The URL was returned by the REST endpoint.