Description
I'm looking to upload images to my remix/ react route v7 application and automatically generate resized versions of the images, and store them all. I was stoked to find this collection of libraries to support file streaming and avoiding buffering entire files, however I'm having a little trouble understanding a non-hacky way to do this. I've previously used Sharp in other projects to handle resizing images and then piping that output stream to something like S3 storage.
Here's what I've been able to quickly come up with, it works, but it seems like a bit of unintentional luck that it works. I'm hoping there's something about this I'm missing that someone can help me work through.
- I'm struggling with the different typings of stream interfaces
- As you can see, it appears to be ignoring/ not needing
byteLength
on my LazyContent objects - I had to cheat by spreading the original FileUpload pointed to the tee'd off stream to avoid stream locks, but I couldn't create a new LazyFile because I didn't seem to have access to the original
byteLength
for that file.
const id = nanoid()
const uploadHandler = async (fileUpload: FileUpload) => {
if (fileUpload.fieldName === 'file') {
const extension = path.extname(fileUpload.name)
const key = `${id}${extension}`
const [ogStream, resizeStream] = fileUpload.stream().tee()
const stream = Readable.fromWeb(resizeStream as ReadableStream<Uint8Array>)
// Use Sharp to resize the image to 256x256 and 1024x1024
const thumbnailStream = stream.pipe(sharp().resize(256, 256, { fit: 'inside' }))
const fullsizeStream = stream.pipe(sharp().resize(1024, 1024, { fit: 'inside' }))
const thumbnailContent: LazyContent = {
byteLength: 1, // This seems to not matter what value we set
stream: () => Readable.toWeb(thumbnailStream) as globalThis.ReadableStream<Uint8Array>
}
const fullsizeContent: LazyContent = {
byteLength: 1, // This seems to not matter what value we set
stream: () => Readable.toWeb(fullsizeStream) as globalThis.ReadableStream<Uint8Array>
}
await Promise.all([
fileStorage.set(key, { ...fileUpload, stream: () => ogStream } as FileUpload), // Everything else is the same, just use the tee'd stream
fileStorage.set(`${key}?w=256`, new LazyFile(thumbnailContent, `${key}?w=256`, { type: fileUpload.type })),
fileStorage.set(`${key}?w=1024`, new LazyFile(fullsizeContent, `${key}?w=1024`, { type: fileUpload.type }))
])
return fileStorage.get(key)
}
}
I'm going to dig into the implementation of LazyFile more and see if I can't answer my own question but on initial usage, this is the best I could come up with.
Thanks in advance for any help!
Activity