Skip to content

Commit

Permalink
feat: use a second PassThrough if the hash will need to read the stre…
Browse files Browse the repository at this point in the history
…am (#578)

* feat: use a second PassThrough if the hash will need to read the stream

* docs: update comment
  • Loading branch information
juanmahidalgo committed Aug 11, 2022
1 parent 64adb28 commit 560f2c8
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/Asset/Asset.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class AssetRouter extends Router {
withAssetPackAuthorization,
asMiddleware(this.assetBelongsToPackMiddleware),
getUploader({
getFileKey: async (file) => {
getFileStreamKey: async (file) => {
const hash = await hashV1(file.stream)
return new S3Content().getFileKey(hash)
},
Expand Down
2 changes: 1 addition & 1 deletion src/Item/Item.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export class ItemRouter extends Router {
withItemExists,
withItemAuthorization,
getUploader({
getFileKey: async (file) => {
getFileStreamKey: async (file) => {
const hash = await hashV1(file.stream)
return new S3Content().getFileKey(hash)
},
Expand Down
3 changes: 2 additions & 1 deletion src/S3/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export type MulterFile = Express.Multer.File
export type GetFileKey = (file: MulterFile, req: Request) => Promise<string>

export type UploaderOptions = Partial<{
getFileKey: GetFileKey
maxFileSize: number
mimeTypes: string[]
getFileKey: GetFileKey
getFileStreamKey: GetFileKey
}>
30 changes: 25 additions & 5 deletions src/S3/uploads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,44 @@ import { GetFileKey, UploaderOptions, MulterFile } from './types'

export function getUploader({
getFileKey,
getFileStreamKey,
maxFileSize = MAX_FILE_SIZE,
mimeTypes,
}: UploaderOptions = {}) {
let options: multer.Options = {
limits: {
fileSize: maxFileSize,
},
storage: new Storage(getFileKey || defaultGetFileKey, mimeTypes),
storage: new Storage(
getFileKey || defaultGetFileKey,
getFileStreamKey,
mimeTypes
),
}
return multer(options)
}

class Storage implements multer.StorageEngine {
constructor(
public getFileKey: GetFileKey,
public getFileStreamKey?: GetFileKey,
public validMimeTypes?: string[]
) {}

getKey(file: MulterFile, req: Request) {
// If "getFileStreamKey" is defined, we need to create a new PassThrough stream since the function
// will need a stream to read from. If not, we assume the function won't use a stream to generate the key.
return this.getFileStreamKey
? this.getFileStreamKey(
{
...file,
stream: file.stream.pipe(new PassThrough()),
},
req
)
: this.getFileKey(file, req)
}

async _handleFile(
req: Request,
file: MulterFile,
Expand All @@ -43,12 +63,12 @@ class Storage implements multer.StorageEngine {

// The original stream comes from file.stream. The reading of the stream is kicked of when we pipe it into the first PassThrough
// This allows for both operations, the upload and the file key generation to be done "simultaneously" without keeping the entire stream data in memory
const fileStream1 = new PassThrough()

const uploadStream = file.stream.pipe(new PassThrough())

const [key] = await Promise.all([
this.getFileKey({ ...file, stream: fileStream1 }, req),
file.stream.pipe(fileStream1),
uploadFile(id, file.stream, ACL.publicRead),
this.getKey(file, req),
uploadFile(id, uploadStream, ACL.publicRead),
])

// move file to key
Expand Down

0 comments on commit 560f2c8

Please sign in to comment.