From 50cf8bda7befe63fd3c77e51f577549142300f93 Mon Sep 17 00:00:00 2001 From: fratzinger <22286818+fratzinger@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:35:16 +0200 Subject: [PATCH] feat: expose streamToGetResult & unify fs & s3 --- src/index.ts | 2 ++ src/services/ServiceFileStreamFS.ts | 26 ++++------------ src/services/ServiceFileStreamS3.ts | 48 ++++++++--------------------- src/utils.ts | 37 ++++++++++++++++++++++ 4 files changed, 57 insertions(+), 56 deletions(-) diff --git a/src/index.ts b/src/index.ts index c85a353..a379a65 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,3 +4,5 @@ export * from "./hooks"; export * from "./middleware"; export * from "./types"; + +export { streamToGetResult } from "./utils"; \ No newline at end of file diff --git a/src/services/ServiceFileStreamFS.ts b/src/services/ServiceFileStreamFS.ts index 06a6806..97c8134 100644 --- a/src/services/ServiceFileStreamFS.ts +++ b/src/services/ServiceFileStreamFS.ts @@ -6,12 +6,11 @@ import path from "node:path"; import streamPomises from "node:stream/promises"; import type { ServiceFileStream, - ServiceFileStreamCreateData, ServiceFileStreamCreateResult, ServiceFileStreamGetResult, } from "../types"; import type { MaybeArray } from "../utility-types"; -import { asArray } from "../utils"; +import { asArray, streamToGetResult } from "../utils"; import mime from "mime-types"; import type { Readable } from "node:stream"; @@ -53,25 +52,12 @@ export class ServiceFileStreamFS implements ServiceFileStream { const { root } = this.options; const stream = createReadStream(path.join(root, id), { start, end }); - const contentType = mime.lookup(id) || "application/octet-stream"; - - // const fileName = path.basename(id); - - return { - header: { - "Accept-Ranges": "bytes", - "Content-Type": contentType, - "Content-disposition": "inline", - "Content-Length": contentLength, - ...(contentRange - ? { - "Content-Range": "bytes " + start + "-" + end + "/" + info.size, - } - : {}), - }, - status: contentRange ? 206 : 200, + return streamToGetResult({ stream, - }; + contentType: mime.lookup(id) || "application/octet-stream", + contentLength, + contentRange: contentRange ? `bytes ${start}-${end}/${contentLength}` : undefined, + }); } async _create( diff --git a/src/services/ServiceFileStreamS3.ts b/src/services/ServiceFileStreamS3.ts index b12ea53..3a02dc1 100644 --- a/src/services/ServiceFileStreamS3.ts +++ b/src/services/ServiceFileStreamS3.ts @@ -21,7 +21,7 @@ import type { ServiceFileStreamGetResult, } from "../types"; import type { MaybeArray } from "../utility-types"; -import { asArray } from "../utils"; +import { asArray, streamToGetResult } from "../utils"; export type ServiceFileStreamS3Options = { s3: S3Client; @@ -33,8 +33,6 @@ export type ServiceFileStreamS3GetParams = { [key: string]: any; }; -export type ServiceFileStreamS3GetResult = ServiceFileStreamGetResult; - export type ServiceFileStreamS3CreateData = ServiceFileStreamCreateData & { size?: number; mimeType?: string; @@ -114,7 +112,7 @@ export class ServiceFileStreamS3 implements ServiceFileStream { async _get( id: string, params?: ServiceFileStreamS3GetParams - ): Promise { + ): Promise { const headResponse = await this.getHeadForObject(id, params); const bucket = params?.bucket || this.bucket; @@ -128,44 +126,22 @@ export class ServiceFileStreamS3 implements ServiceFileStream { Range: range, }; - const header: Record = { - ETag: headResponse.ETag, - "Content-Disposition": "inline", - }; - - if (headResponse.ContentLength) { - header["Content-Length"] = headResponse.ContentLength; - } - - if (headResponse.ContentType) { - header["Content-Type"] = headResponse.ContentType; - } - - if (headResponse.ContentEncoding) { - header["Content-Encoding"] = headResponse.ContentEncoding; - } - - if (headResponse.AcceptRanges) { - header["Accept-Ranges"] = headResponse.AcceptRanges; - } - // Now get the object data and stream it const response = await s3.send(new GetObjectCommand(params)); - let status = 200; - - if (response.ContentRange) { - header["Content-Range"] = response.ContentRange; - status = 206; - } - const stream = response.Body as Readable; - return { - header, + return streamToGetResult({ + header: { + ETag: headResponse.ETag, + "Content-Encoding": headResponse.ContentEncoding, + "Accept-Ranges": headResponse.AcceptRanges, + }, + contentLength: headResponse.ContentLength, + contentType: headResponse.ContentType, + contentRange: response.ContentRange, stream, - status, - } as ServiceFileStreamS3GetResult; + }); } catch (err) { this.errorHandler(err); throw err; diff --git a/src/utils.ts b/src/utils.ts index 25a5273..aead3d5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,4 @@ +import type { Readable } from "node:stream"; import type { ServiceFileStreamGetResult } from "./types"; import type { MaybeArray } from "./utility-types"; @@ -20,3 +21,39 @@ export const asArray = ( export const isGetResult = (data: any): data is ServiceFileStreamGetResult => { return data?.stream !== undefined; }; + +type StreamToGetResult = { + stream: Readable; + contentType: string | undefined; + contentLength: number | undefined; + contentRange?: string | undefined; + header?: Record; +} + +export const streamToGetResult = (options: StreamToGetResult): ServiceFileStreamGetResult => { + const header = {}; + + for (const key in options.header) { + if (options.header[key] !== undefined) { + header[key] = options.header[key]; + } + } + + return { + header: { + ...header, + "Accept-Ranges": "bytes", + ...(options.contentType !== undefined ? { "Content-Type": options.contentType } : {}), + "Content-disposition": "inline", + ...(options.contentLength !== undefined ? { "Content-Length": options.contentLength } : {}), + "Content-Length": options.contentLength, + ...(options.contentRange !== undefined && options.contentLength !== undefined + ? { + "Content-Range": options.contentRange, + } + : {}), + }, + status: options.contentRange ? 206 : 200, + stream: options.stream, + }; +}; \ No newline at end of file