fix(storage-uploadthing): files are duplicated to the storage via client uploads (#11518)

When uploading file via client side upload we invalidate it then on the
server side with re-uploading. This works fine with most adapters since
they just replace the old file under the same key. UploadThing works
differently and generates a new key every time.

Example of the issue:
<img width="611" alt="image"
src="https://github.com/user-attachments/assets/9c01b52a-d159-4f32-9f66-3b5fbadab7b4"
/>

Now, we clear the old file before doing re-upload.
This commit is contained in:
Sasha
2025-03-04 16:57:30 +02:00
committed by GitHub
parent 7d2480aef9
commit f143d25728
6 changed files with 26 additions and 2 deletions

View File

@@ -90,6 +90,10 @@ type PayloadRequestData = {
data?: JsonObject data?: JsonObject
/** The file on the request, same rules apply as the `data` property */ /** The file on the request, same rules apply as the `data` property */
file?: { file?: {
/**
* Context of the file when it was uploaded via client side.
*/
clientUploadContext?: unknown
data: Buffer data: Buffer
mimetype: string mimetype: string
name: string name: string

View File

@@ -87,6 +87,7 @@ export const addDataAndFileToRequest: AddDataAndFileToRequest = async (req) => {
req.file = { req.file = {
name: filename, name: filename,
clientUploadContext,
data: Buffer.from(await response.arrayBuffer()), data: Buffer.from(await response.arrayBuffer()),
mimetype: response.headers.get('Content-Type') || mimeType, mimetype: response.headers.get('Content-Type') || mimeType,
size, size,

View File

@@ -44,7 +44,13 @@ export const getBeforeChangeHook =
} }
const promises = files.map(async (file) => { const promises = files.map(async (file) => {
await adapter.handleUpload({ collection, data, file, req }) await adapter.handleUpload({
clientUploadContext: file.clientUploadContext,
collection,
data,
file,
req,
})
}) })
await Promise.all(promises) await Promise.all(promises)

View File

@@ -10,6 +10,7 @@ import type {
export interface File { export interface File {
buffer: Buffer buffer: Buffer
clientUploadContext?: unknown
filename: string filename: string
filesize: number filesize: number
mimeType: string mimeType: string
@@ -28,6 +29,7 @@ export type ClientUploadsConfig =
| boolean | boolean
export type HandleUpload = (args: { export type HandleUpload = (args: {
clientUploadContext: unknown
collection: CollectionConfig collection: CollectionConfig
data: any data: any
file: File file: File

View File

@@ -16,6 +16,7 @@ export function getIncomingFiles({
if (file && data.filename && data.mimeType) { if (file && data.filename && data.mimeType) {
const mainFile: File = { const mainFile: File = {
buffer: file.data, buffer: file.data,
clientUploadContext: file.clientUploadContext,
filename: data.filename, filename: data.filename,
filesize: file.size, filesize: file.size,
mimeType: data.mimeType, mimeType: data.mimeType,

View File

@@ -12,8 +12,18 @@ type HandleUploadArgs = {
} }
export const getHandleUpload = ({ acl, utApi }: HandleUploadArgs): HandleUpload => { export const getHandleUpload = ({ acl, utApi }: HandleUploadArgs): HandleUpload => {
return async ({ data, file }) => { return async ({ clientUploadContext, data, file }) => {
try { try {
if (
clientUploadContext &&
typeof clientUploadContext === 'object' &&
'key' in clientUploadContext &&
typeof clientUploadContext.key === 'string'
) {
// Clear the old file
await utApi.deleteFiles(clientUploadContext.key)
}
const { buffer, filename, mimeType } = file const { buffer, filename, mimeType } = file
const blob = new Blob([buffer], { type: mimeType }) const blob = new Blob([buffer], { type: mimeType })