UNPKG

@opengis/fastify-table

Version:

core-plugins

113 lines (112 loc) 4.87 kB
/* 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; }