@@ -11,8 +11,16 @@ import { FastifyInstance, FastifyPluginAsync } from "fastify";
1111import fp from "fastify-plugin" ;
1212import { streamToBuffer } from "./utils" ;
1313
14+ export interface FileInfo {
15+ size : number ;
16+ contentType : string ;
17+ lastModified : Date ;
18+ }
19+
1420export interface FileStore {
1521 exists ( filepath : string ) : Promise < boolean > ;
22+ // return null if file does not exist
23+ getInfo ( filepath : string ) : Promise < FileInfo | null > ;
1624 save (
1725 filepath : string ,
1826 contentType : string ,
@@ -56,6 +64,23 @@ class LocalFileStore implements FileStore {
5664 const p = path . join ( this . dir , filepath ) ;
5765 return ! ! ( await fs . promises . stat ( p ) ) ;
5866 }
67+ async getInfo ( filepath : string ) : Promise < FileInfo | null > {
68+ try {
69+ const p = path . join ( this . dir , filepath ) ;
70+ const stat = await fs . promises . stat ( p ) ;
71+ return {
72+ size : stat . size ,
73+ contentType : "application/octet-stream" ,
74+ lastModified : stat . mtime ,
75+ } ;
76+ } catch ( err ) {
77+ if ( err . code === "ENOENT" ) {
78+ return null ;
79+ }
80+ throw err ;
81+ }
82+ }
83+
5984 async save (
6085 filepath : string ,
6186 _contentType : string ,
@@ -179,6 +204,24 @@ class AzureFileStore implements FileStore {
179204 throw new Error ( resp . errorCode ) ;
180205 }
181206 }
207+
208+ async getInfo ( filepath : string ) : Promise < FileInfo | null > {
209+ try {
210+ const blob = this . client . getBlobClient ( filepath ) ;
211+ const properties = await blob . getProperties ( ) ;
212+
213+ return {
214+ size : properties . contentLength || 0 ,
215+ contentType : properties . contentType || "application/octet-stream" ,
216+ lastModified : properties . lastModified || new Date ( ) ,
217+ } ;
218+ } catch ( err ) {
219+ if ( err . statusCode === 404 ) {
220+ return null ;
221+ }
222+ throw err ;
223+ }
224+ }
182225}
183226
184227class GCPFileStore implements FileStore {
@@ -239,6 +282,29 @@ class GCPFileStore implements FileStore {
239282 } ) ,
240283 ) ;
241284 }
285+
286+ async getInfo ( filepath : string ) : Promise < FileInfo | null > {
287+ try {
288+ const gcsfile = this . storage . bucket ( this . bucket ) . file ( filepath ) ;
289+ const [ metadata ] = await gcsfile . getMetadata ( ) ;
290+
291+ return {
292+ size :
293+ typeof metadata . size === "number"
294+ ? metadata . size
295+ : parseInt ( metadata . size ?? "0" , 10 ) || 0 ,
296+ contentType : metadata . contentType || "application/octet-stream" ,
297+ lastModified : metadata . updated
298+ ? new Date ( metadata . updated )
299+ : new Date ( ) ,
300+ } ;
301+ } catch ( err ) {
302+ if ( err . code === 404 ) {
303+ return null ;
304+ }
305+ throw err ;
306+ }
307+ }
242308}
243309
244310class S3FileStore implements FileStore {
@@ -334,6 +400,31 @@ class S3FileStore implements FileStore {
334400 } ) ,
335401 ) ;
336402 }
403+
404+ async getInfo ( filepath : string ) : Promise < FileInfo | null > {
405+ try {
406+ const data = await this . client . send (
407+ new S3 . HeadObjectCommand ( {
408+ Bucket : this . bucket ,
409+ Key : filepath ,
410+ } ) ,
411+ ) ;
412+
413+ return {
414+ size : data . ContentLength || 0 ,
415+ contentType : data . ContentType || "application/octet-stream" ,
416+ lastModified : data . LastModified || new Date ( ) ,
417+ } ;
418+ } catch ( err ) {
419+ if (
420+ err instanceof S3 . NoSuchKey ||
421+ err [ "$metadata" ] ?. httpStatusCode === 404
422+ ) {
423+ return null ;
424+ }
425+ throw err ;
426+ }
427+ }
337428}
338429
339430async function ConfigureLocal ( f : FastifyInstance ) {
0 commit comments