fix(storage-s3, storage-azure, storage-gcs): client uploads when a collection has prefix configured (#11436)

### What?
Fixes client uploads when storage collection config has the `prefix`
property configured. Previously, it failed with "Object key was not
found".

### Why?
This is expected to work.

### How?
The client upload handler now receives to its props `prefix`. Then it
threads it to the server-side `staticHandler` through
`clientUploadContext` and then to `getFilePrefix`, which checks for
`clientUploadContext.prefix` and returns if there is.

Previously, `staticHandler` tried to load the file without including
prefix consideration.

This changes only these adapters:
* S3
* Azure
* GCS

With the Vercel Blob adapter, `prefix` works correctly.
This commit is contained in:
Sasha
2025-02-28 22:50:23 +02:00
committed by GitHub
parent 83b4548fc1
commit fc42c40883
9 changed files with 45 additions and 9 deletions

View File

@@ -10,6 +10,7 @@ type ClientUploadHandlerProps<T extends Record<string, unknown>> = {
collectionSlug: UploadCollectionSlug
enabled?: boolean
extra: T
prefix?: string
serverHandlerPath: string
}
@@ -21,6 +22,7 @@ export const createClientUploadHandler = <T extends Record<string, unknown>>({
collectionSlug: UploadCollectionSlug
extra: T
file: File
prefix?: string
serverHandlerPath: string
serverURL: string
updateFilename: (value: string) => void
@@ -31,6 +33,7 @@ export const createClientUploadHandler = <T extends Record<string, unknown>>({
collectionSlug,
enabled,
extra,
prefix,
serverHandlerPath,
}: ClientUploadHandlerProps<T>) {
const { setUploadHandler } = useUploadHandlers()
@@ -51,6 +54,7 @@ export const createClientUploadHandler = <T extends Record<string, unknown>>({
collectionSlug,
extra,
file,
prefix,
serverHandlerPath,
serverURL,
updateFilename,

View File

@@ -1,14 +1,26 @@
import type { CollectionConfig, PayloadRequest, UploadConfig } from 'payload'
export async function getFilePrefix({
clientUploadContext,
collection,
filename,
req,
}: {
clientUploadContext?: unknown
collection: CollectionConfig
filename: string
req: PayloadRequest
}): Promise<string> {
// Prioritize from clientUploadContext if there is:
if (
clientUploadContext &&
typeof clientUploadContext === 'object' &&
'prefix' in clientUploadContext &&
typeof clientUploadContext.prefix === 'string'
) {
return clientUploadContext.prefix
}
const imageSizes = (collection?.upload as UploadConfig)?.imageSizes || []
const files = await req.payload.find({

View File

@@ -63,11 +63,23 @@ export const initClientUploads = <ExtraProps extends Record<string, unknown>, T>
for (const collectionSlug in collections) {
const collection = collections[collectionSlug]
let prefix: string | undefined
if (
collection &&
typeof collection === 'object' &&
'prefix' in collection &&
typeof collection.prefix === 'string'
) {
prefix = collection.prefix
}
config.admin.components.providers.push({
clientProps: {
collectionSlug,
enabled,
extra: extraClientHandlerProps ? extraClientHandlerProps(collection) : undefined,
prefix,
serverHandlerPath,
},
path: clientHandler,

View File

@@ -2,7 +2,7 @@
import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'
export const AzureClientUploadHandler = createClientUploadHandler({
handler: async ({ apiRoute, collectionSlug, file, serverHandlerPath, serverURL }) => {
handler: async ({ apiRoute, collectionSlug, file, prefix, serverHandlerPath, serverURL }) => {
const response = await fetch(`${serverURL}${apiRoute}${serverHandlerPath}`, {
body: JSON.stringify({
collectionSlug,
@@ -25,5 +25,7 @@ export const AzureClientUploadHandler = createClientUploadHandler({
},
method: 'PUT',
})
return { prefix }
},
})

View File

@@ -13,9 +13,9 @@ interface Args {
}
export const getHandler = ({ collection, getStorageClient }: Args): StaticHandler => {
return async (req, { params: { filename } }) => {
return async (req, { params: { clientUploadContext, filename } }) => {
try {
const prefix = await getFilePrefix({ collection, filename, req })
const prefix = await getFilePrefix({ clientUploadContext, collection, filename, req })
const blockBlobClient = getStorageClient().getBlockBlobClient(
path.posix.join(prefix, filename),
)

View File

@@ -2,7 +2,7 @@
import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'
export const GcsClientUploadHandler = createClientUploadHandler({
handler: async ({ apiRoute, collectionSlug, file, serverHandlerPath, serverURL }) => {
handler: async ({ apiRoute, collectionSlug, file, prefix, serverHandlerPath, serverURL }) => {
const response = await fetch(`${serverURL}${apiRoute}${serverHandlerPath}`, {
body: JSON.stringify({
collectionSlug,
@@ -20,5 +20,9 @@ export const GcsClientUploadHandler = createClientUploadHandler({
headers: { 'Content-Length': file.size.toString(), 'Content-Type': file.type },
method: 'PUT',
})
return {
prefix,
}
},
})

View File

@@ -12,9 +12,9 @@ interface Args {
}
export const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => {
return async (req, { params: { filename } }) => {
return async (req, { params: { clientUploadContext, filename } }) => {
try {
const prefix = await getFilePrefix({ collection, filename, req })
const prefix = await getFilePrefix({ clientUploadContext, collection, filename, req })
const file = getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename))
const [metadata] = await file.getMetadata()

View File

@@ -2,7 +2,7 @@
import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'
export const S3ClientUploadHandler = createClientUploadHandler({
handler: async ({ apiRoute, collectionSlug, file, serverHandlerPath, serverURL }) => {
handler: async ({ apiRoute, collectionSlug, file, prefix, serverHandlerPath, serverURL }) => {
const response = await fetch(`${serverURL}${apiRoute}${serverHandlerPath}`, {
body: JSON.stringify({
collectionSlug,
@@ -20,5 +20,7 @@ export const S3ClientUploadHandler = createClientUploadHandler({
headers: { 'Content-Length': file.size.toString(), 'Content-Type': file.type },
method: 'PUT',
})
return { prefix }
},
})

View File

@@ -35,9 +35,9 @@ const streamToBuffer = async (readableStream: any) => {
}
export const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => {
return async (req, { params: { filename } }) => {
return async (req, { params: { clientUploadContext, filename } }) => {
try {
const prefix = await getFilePrefix({ collection, filename, req })
const prefix = await getFilePrefix({ clientUploadContext, collection, filename, req })
const key = path.posix.join(prefix, filename)