Skip to content

Instantly share code, notes, and snippets.

@pdaug
Created May 16, 2025 21:10
Show Gist options
  • Save pdaug/d04a6aed7a3c174bd34a57fa90bb1d9a to your computer and use it in GitHub Desktop.
Save pdaug/d04a6aed7a3c174bd34a57fa90bb1d9a to your computer and use it in GitHub Desktop.
import sharp from "sharp";
import fileTyper, { FileTypeResult } from "file-type";
import { statSync, readFileSync, existsSync } from "fs";
// utils
import Hash from "./Hash";
import Logger from "./Logger";
export type MetadataInfo = {
size: number;
updatedAt: Date;
createdAt: Date;
accessedAt: Date;
};
export type MetadataImage = {
size: number;
updatedAt: Date;
createdAt: Date;
accessedAt: Date;
};
class Metadata {
pathOrSource: string | Buffer;
logger: Logger;
constructor(pathOrSource: string | Buffer) {
this.pathOrSource = pathOrSource;
this.logger = new Logger("src/utils/Metadata.ts");
return;
}
info(): MetadataInfo | null {
if (!this.pathOrSource) return null;
if (typeof this.pathOrSource === "string" && existsSync(this.pathOrSource)) {
const stats = statSync(this.pathOrSource);
const info = {
size: stats.size,
updatedAt: new Date(stats.mtimeMs),
createdAt: new Date(stats.birthtimeMs),
accessedAt: new Date(stats.atimeMs),
};
return info;
}
this.logger.debug("warn", "no_info_path", this.pathOrSource);
this.logger.send({
action: "METADATA",
});
return null;
}
async md5(): Promise<string | null> {
if (!this.pathOrSource) return null;
const fileSource = typeof this.pathOrSource === "string" ? readFileSync(this.pathOrSource) : this.pathOrSource;
const hasher = new Hash("md5", fileSource);
const fileHashed = await hasher.make();
return fileHashed;
}
async mimetype(): Promise<FileTypeResult | null> {
if (!this.pathOrSource) return null;
try {
let result = null;
if (this.pathOrSource instanceof Buffer) result = await fileTyper.fromBuffer(this.pathOrSource);
if (typeof this.pathOrSource === "string") result = await fileTyper.fromFile(this.pathOrSource);
return result || null;
} catch (err) {
this.logger.debug("error", "failed_load_metadata", err);
await this.logger.send({
action: "METADATA",
err,
});
return null;
}
}
async image(): Promise<sharp.Metadata | null> {
if (!this.pathOrSource) return null;
try {
const result = await this.mimetype();
if (!result || !result?.mime?.includes("image/")) {
this.logger.debug("warn", "not_image", result);
this.logger.send({
action: "METADATA",
});
return null;
}
const sharpInstance = sharp(this.pathOrSource);
const metadata = await sharpInstance.metadata();
return metadata;
} catch (err) {
this.logger.debug("error", "failed_image_metadata", err);
await this.logger.send({
action: "METADATA",
err,
});
return null;
}
}
}
export default Metadata;
describe("Metadata", function () {
it("should get info", async function () {
const metadataInstance = new Metadata("./src/public/logo.png");
const info = metadataInstance.info();
expect(info).toBeTruthy();
expect(info).not.toBeNull();
expect(typeof info?.size).toBe("number");
expect(info?.updatedAt instanceof Date).toBeTruthy();
expect(info?.createdAt instanceof Date).toBeTruthy();
expect(info?.accessedAt instanceof Date).toBeTruthy();
return;
});
it("should get md5", async function () {
const metadataInstance = new Metadata("./src/public/logo.png");
const md5 = await metadataInstance.md5();
expect(md5).toBeTruthy();
expect(md5).not.toBeNull();
expect(typeof md5).toBe("string");
return;
});
it("should get mimetype", async function () {
const metadataInstance = new Metadata("./src/public/logo.png");
const mimetype = await metadataInstance.mimetype();
expect(mimetype).toBeTruthy();
expect(mimetype).not.toBeNull();
expect(mimetype?.ext).toBe("png");
expect(mimetype?.mime).toBe("image/png");
return;
});
it("should get image metadata", async function () {
const metadataInstance = new Metadata("./src/public/logo.png");
const imageMetadata = await metadataInstance.image();
expect(imageMetadata).toBeTruthy();
expect(imageMetadata).not.toBeNull();
expect(imageMetadata?.format).toBe("png");
expect(typeof imageMetadata?.width).toBe("number");
expect(typeof imageMetadata?.height).toBe("number");
return;
});
return;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment