Files
tinypin/server/image-utils.js
2021-01-29 14:23:37 -06:00

113 lines
3.9 KiB
JavaScript

const sharp = require('sharp');
const fetch = require('node-fetch');
const fs = require('fs').promises;
const conf = require("./conf.js");
const THUMBNAIL_IMAGE_SIZE = 400;
const ADDITIONAL_IMAGE_SIZES = [800,1280,1920,2560];
/**
* Downloads the image, converts it to JPG, and creates the thumbnail size so that standard dimensions can be taken
* @param {string} imageUrl
*/
async function downloadImage(imageUrl){
let res = await fetch(imageUrl);
if ( res.status != 200 ){
throw(new Error(`download error status=${res.status}`));
}
let buffer = await res.buffer();
return await processImage(buffer);
}
async function processImage(buffer){
let original = sharp(buffer);
let originalBuffer = await original.toFormat("jpg").toBuffer();
let originalMetadata = await original.metadata();
let thumbnail = await original.resize({ width: THUMBNAIL_IMAGE_SIZE, height: THUMBNAIL_IMAGE_SIZE, fit: 'inside' });
let thumbnailBuffer = await thumbnail.toBuffer();
let thumbnailMetadata = await sharp(thumbnailBuffer).metadata();
return {
original: {
buffer: originalBuffer,
width: originalMetadata.width,
height: originalMetadata.height
},
thumbnail: {
buffer: thumbnailBuffer,
width: thumbnailMetadata.width,
height: thumbnailMetadata.height
}
}
}
// takes the response from downloadImage, creates ADDITIONAL_IMAGE_SIZE and writes all files to disk
async function saveImage(userId, pinId, image){
let originalImagePath = getImagePath(userId, pinId, 'o');
await fs.mkdir(originalImagePath.dir, {recursive: true});
await fs.writeFile(originalImagePath.file, image.original.buffer);
console.log(`saved original to: ${originalImagePath.file}`);
let thumbnailImagePath = getImagePath(userId, pinId, '400');
await fs.mkdir(thumbnailImagePath.dir, {recursive: true});
await fs.writeFile(thumbnailImagePath.file, image.thumbnail.buffer);
console.log(`saved thumbnail to: ${thumbnailImagePath.file}`);
// this will enlarge images if necessary, as the lanczos3 resize algorithm will create better looking enlargements than the browser will
for ( let i = 0; i < ADDITIONAL_IMAGE_SIZES.length; ++i ){
let size = ADDITIONAL_IMAGE_SIZES[i];
let img = await sharp(image.original.buffer);
let resizedImg = await img.resize({width: size, height: size, fit: 'inside'})
let buf = await resizedImg.toBuffer();
let imgPath = getImagePath(userId, pinId, size);
await fs.mkdir(imgPath.dir, {recursive:true});
await fs.writeFile(imgPath.file, buf);
console.log(`saved additional size ${size} to: ${imgPath.file}`);
}
}
async function deleteImagesForPin(userId, pinId){
console.log("deleting images for userId=" + userId + " pinId=" + pinId);
try {
await fs.unlink(getImagePath(userId, pinId, 'o').file);
} catch (err){
console.log("error deleting original: ", err);
}
try {
await fs.unlink(getImagePath(userId, pinId, THUMBNAIL_IMAGE_SIZE).file);
} catch (err){
console.log("error deleting thumbnail: " + err);
}
for ( let s = 0; s < ADDITIONAL_IMAGE_SIZES.length; ++s ){
try{
await fs.unlink(getImagePath(userId, pinId, ADDITIONAL_IMAGE_SIZES[s]).file);
} catch (err){
console.log("error deleting additional size " + ADDITIONAL_IMAGE_SIZES[s] + ": ", err);
}
}
}
function getImagePath(userId, pinId, size){
let paddedId = pinId.toString().padStart(12, '0');
let dir = `${conf.getImagePath()}/${userId}/images/${size}/${paddedId[11]}/${paddedId[10]}/${paddedId[9]}/${paddedId[8]}`;
let file = `${dir}/${paddedId}.jpg`;
return { dir: dir, file: file};
}
module.exports = {
processImage: processImage,
saveImage: saveImage,
getImagePath: getImagePath,
downloadImage: downloadImage,
deleteImagesForPin: deleteImagesForPin
}