@opengis/fastify-table
Version:
core-plugins
113 lines (112 loc) • 4.87 kB
JavaScript
/* eslint-disable no-console */
import path from "node:path";
import { mkdir, writeFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";
import { imageSize } from "image-size";
import { existsSync } from "node:fs";
import config from "../../../config.js";
import providers from "./providers/index.js";
import { all, images } from "./utils/allowedExtensions.js";
import grpc from "../grpc/grpc.js";
import getFileType from "./utils/getFileType.js";
import { BadRequestError, PayloadTooLargeError } from "../../types/errors.js";
const { resizeImage } = grpc();
const { resizeImageMinSize = 5 } = config; // resize images >= 5 MB by default
async function writeFileToDisk(file, buffer) {
if (!file?.filepath || !file.extension || !buffer) {
return null;
}
// resize big images
if (images.find((el) => el === file.extension && !["tif", "tiff"].includes(file.extension)) &&
file.size >= resizeImageMinSize * 1024 * 1024) {
const { width = 320, height = 240 } = imageSize(buffer) || {};
const ratio = width / height;
const resizeWidth = Math.min(width, 2048);
const resizeHeight = resizeWidth / ratio;
const { result } = await resizeImage({
base64: buffer.toString("base64"),
width: resizeWidth,
height: resizeHeight,
quality: 75,
});
await writeFile(`${file.filepath.replace(`.${file.extension}`, `_original.${file.extension}`)}`, buffer);
await writeFile(file.filepath, Buffer.from(result, "base64"));
return null;
}
await writeFile(file.filepath, buffer);
return null;
}
export default async function uploadMultiPart(req, { extensions, raw, subdir, originalFilename = false, } = {}) {
const allowedExtensions = req.routeOptions?.url === "/file/upload-image/*" ? images : all;
const parts = req.parts();
// const part = await parts.next();
const { value: part, done } = await parts.next();
if (done) {
throw new Error("parts already processed");
}
const value = part?.fields?.file || part;
if (!value?.file?.on || !value?.filename) {
throw new Error("upload error");
}
const ext = path.extname(value.filename).toLowerCase();
// check extension by custom list
if (extensions && !extensions.includes(ext)) {
throw new BadRequestError("file extension is not allowed");
}
// check extension by core list if custom not provided
if (!extensions && !allowedExtensions.includes(ext.substring(1))) {
throw new BadRequestError("file extension is not allowed");
}
const fileBuffer = await new Promise((res, rej) => {
const chunks = [];
value.file.on("data", (chunk) => chunks.push(chunk));
value.file.on("end", () => res(Buffer.concat(chunks)));
value.file.on("error", rej);
value.file.on("limit", () => rej(new PayloadTooLargeError("file size exceeds limit")));
});
if (!fileBuffer?.length) {
throw new BadRequestError("file buffer is empty");
}
// return buffer for custom upload
if (raw) {
return {
buffer: fileBuffer,
filename: value.filename,
extension: ext.substring(1),
fields: value.fields,
};
}
const dir = subdir != null ? subdir : req.params?.["*"] || "uploads";
const yearMonthDay = subdir != null ? "" : new Date().toISOString().split("T")[0];
const dbname = req.pg?.options?.database || req.pg?.database || config.pg?.database; // request / config params / default config params
const rootDir = config.root || `/data/local/${dbname || ""}`;
const reldirpath = path.join("/files", dir, yearMonthDay);
const folder = path.join(rootDir, config.folder || "", reldirpath);
const newFilename = originalFilename
? value.filename
: `${randomUUID()}${ext}`;
if (existsSync(path.join(folder, newFilename).replace(/\\/g, "/"))) {
throw new BadRequestError("file with specified name already exists in directory");
}
const file = {
fields: value.fields,
originalFilename: value.filename,
newFilename,
filepath: path.join(folder, newFilename).replace(/\\/g, "/"),
filetype: getFileType(newFilename),
relativeFilepath: path.join(reldirpath, newFilename).replace(/\\/g, "/"),
size: Buffer.byteLength(fileBuffer),
mimetype: value.mimetype,
extension: ext.substring(1),
};
await mkdir(folder, { recursive: true });
await writeFileToDisk(file, fileBuffer);
// move file to s3
if (config.s3?.endpoint) {
const s3 = providers();
await s3.uploadFile(file.relativeFilepath, fileBuffer);
if (config.trace)
console.log("upload to s3", file.relativeFilepath);
}
return file;
}