Skip to content

Commit 037debb

Browse files
committed
fix(storage): include metadata in uploadToSignedUrl request
The uploadToSignedUrl method accepted metadata in fileOptions but never used it. This copies the metadata handling from uploadOrUpdate into uploadToSignedUrl for all three body types (Blob, FormData, raw body). Also forwards custom fileOptions.headers, matching uploadOrUpdate behavior. Fixes supabase/storage#823
1 parent c338e2f commit 037debb

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

packages/core/storage-js/src/packages/StorageFileApi.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,23 +252,41 @@ export default class StorageFileApi extends BaseApiClient<StorageError> {
252252

253253
return this.handleOperation(async () => {
254254
let body
255-
const options = { upsert: DEFAULT_FILE_OPTIONS.upsert, ...fileOptions }
256-
const headers: Record<string, string> = {
255+
const options = { ...DEFAULT_FILE_OPTIONS, upsert: DEFAULT_FILE_OPTIONS.upsert, ...fileOptions }
256+
let headers: Record<string, string> = {
257257
...this.headers,
258258
...{ 'x-upsert': String(options.upsert as boolean) },
259259
}
260260

261+
const metadata = options.metadata
262+
261263
if (typeof Blob !== 'undefined' && fileBody instanceof Blob) {
262264
body = new FormData()
263265
body.append('cacheControl', options.cacheControl as string)
266+
if (metadata) {
267+
body.append('metadata', this.encodeMetadata(metadata))
268+
}
264269
body.append('', fileBody)
265270
} else if (typeof FormData !== 'undefined' && fileBody instanceof FormData) {
266271
body = fileBody
267-
body.append('cacheControl', options.cacheControl as string)
272+
if (!body.has('cacheControl')) {
273+
body.append('cacheControl', options.cacheControl as string)
274+
}
275+
if (metadata && !body.has('metadata')) {
276+
body.append('metadata', this.encodeMetadata(metadata))
277+
}
268278
} else {
269279
body = fileBody
270280
headers['cache-control'] = `max-age=${options.cacheControl}`
271281
headers['content-type'] = options.contentType as string
282+
283+
if (metadata) {
284+
headers['x-metadata'] = this.toBase64(this.encodeMetadata(metadata))
285+
}
286+
}
287+
288+
if (fileOptions?.headers) {
289+
headers = { ...headers, ...fileOptions.headers }
272290
}
273291

274292
const data = await put(this.fetch, url.toString(), body as object, { headers })

packages/core/storage-js/test/storageFileApi.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,52 @@ describe('StorageFileApi Edge Cases', () => {
920920
expect(body).toBe(testFormData)
921921
})
922922

923+
test('uploadToSignedUrl with Blob includes metadata in FormData', async () => {
924+
const testBlob = new Blob(['test content'], { type: 'text/plain' })
925+
const metadata = { customKey: 'customValue', author: 'test' }
926+
927+
await storage
928+
.from('test-bucket')
929+
.uploadToSignedUrl('test-path', 'test-token', testBlob, { metadata })
930+
931+
expect(mockPut).toHaveBeenCalled()
932+
const [, , body] = mockPut.mock.calls[0] as [null, null, FormData]
933+
expect(body.constructor.name).toBe('FormData')
934+
expect(body.get('metadata')).toBe(JSON.stringify(metadata))
935+
})
936+
937+
test('uploadToSignedUrl with FormData includes metadata', async () => {
938+
const testFormData = new FormData()
939+
testFormData.append('file', 'test content')
940+
const metadata = { customKey: 'customValue' }
941+
942+
await storage
943+
.from('test-bucket')
944+
.uploadToSignedUrl('test-path', 'test-token', testFormData, { metadata })
945+
946+
expect(mockPut).toHaveBeenCalled()
947+
const [, , body] = mockPut.mock.calls[0] as [null, null, FormData]
948+
expect(body).toBe(testFormData)
949+
expect(body.get('metadata')).toBe(JSON.stringify(metadata))
950+
})
951+
952+
test('uploadToSignedUrl with raw body includes metadata in headers', async () => {
953+
const testBody = 'raw string content'
954+
const metadata = { customKey: 'customValue' }
955+
956+
await storage
957+
.from('test-bucket')
958+
.uploadToSignedUrl('test-path', 'test-token', testBody, { metadata })
959+
960+
expect(mockPut).toHaveBeenCalled()
961+
const [, , , { headers }] = mockPut.mock.calls[0]
962+
const expectedBase64 =
963+
typeof Buffer !== 'undefined'
964+
? Buffer.from(JSON.stringify(metadata)).toString('base64')
965+
: btoa(JSON.stringify(metadata))
966+
expect(headers['x-metadata']).toBe(expectedBase64)
967+
})
968+
923969
test('upload with metadata', async () => {
924970
const testBlob = new Blob(['test content'], { type: 'text/plain' })
925971
const metadata = { customKey: 'customValue', author: 'test' }

0 commit comments

Comments
 (0)