Skip to content
This repository was archived by the owner on Jan 16, 2024. It is now read-only.

Commit 4c8f33e

Browse files
authored
feat(s3): support large uploads (#37)
* feat(s3): implement multipart upload * feat(s3): support tempFilePath and using read stream for uploads
1 parent e11a0fb commit 4c8f33e

7 files changed

Lines changed: 44 additions & 8 deletions

File tree

dev/src/mocks/fileStub.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 'file-stub'

dev/src/mocks/promisifyMock.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const promisify = () => {}

dev/src/payload.config.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { Adapter } from '../../src/types'
99
import { Media } from './collections/Media'
1010

1111
let adapter: Adapter
12+
let uploadOptions
1213

1314
if (process.env.PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER === 'azure') {
1415
adapter = azureBlobStorageAdapter({
@@ -20,6 +21,11 @@ if (process.env.PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER === 'azure') {
2021
}
2122

2223
if (process.env.PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER === 's3') {
24+
// The s3 adapter supports using temp files for uploads
25+
uploadOptions = {
26+
useTempFiles: true,
27+
}
28+
2329
adapter = s3Adapter({
2430
config: {
2531
endpoint: process.env.S3_ENDPOINT,
@@ -47,6 +53,7 @@ if (process.env.PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER === 'gcs') {
4753
export default buildConfig({
4854
serverURL: 'http://localhost:3000',
4955
collections: [Media, Users],
56+
upload: uploadOptions,
5057
admin: {
5158
// NOTE - these webpack extensions are only required
5259
// for development of this plugin.
@@ -59,6 +66,8 @@ export default buildConfig({
5966
alias: {
6067
...(config.resolve.alias || {}),
6168
react: path.resolve(__dirname, '../node_modules/react'),
69+
fs: path.resolve(__dirname, 'mocks/fileStub.js'),
70+
util: path.resolve(__dirname, 'mocks/promisifyMock.js'),
6271
'@azure/storage-blob': path.resolve(__dirname, '../../src/adapters/azure/mock.js'),
6372
'@aws-sdk/client-s3': path.resolve(__dirname, '../../src/adapters/s3/mock.js'),
6473
'@google-cloud/storage': path.resolve(__dirname, '../../src/adapters/gcs/mock.js'),
@@ -72,6 +81,7 @@ export default buildConfig({
7281
outputFile: path.resolve(__dirname, 'payload-types.ts'),
7382
},
7483
plugins: [
84+
// @ts-expect-error
7585
cloudStorage({
7686
collections: {
7787
media: {
@@ -81,12 +91,19 @@ export default buildConfig({
8191
}),
8292
],
8393
onInit: async payload => {
84-
await payload.create({
94+
const users = await payload.find({
8595
collection: 'users',
86-
data: {
87-
email: 'dev@payloadcms.com',
88-
password: 'test',
89-
},
96+
limit: 1,
9097
})
98+
99+
if (!users.docs.length) {
100+
await payload.create({
101+
collection: 'users',
102+
data: {
103+
email: 'dev@payloadcms.com',
104+
password: 'test',
105+
},
106+
})
107+
}
91108
},
92109
})

src/adapters/s3/handleUpload.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import fs from 'fs'
12
import path from 'path'
23
import type * as AWS from '@aws-sdk/client-s3'
34
import type { CollectionConfig } from 'payload/types'
5+
import type stream from 'stream'
46
import type { HandleUpload } from '../../types'
57

68
interface Args {
@@ -18,10 +20,19 @@ export const getHandleUpload = ({
1820
prefix = '',
1921
}: Args): HandleUpload => {
2022
return async ({ data, file }) => {
23+
const fileKey = path.posix.join(prefix, file.filename)
24+
25+
let fileBufferOrStream: Buffer | stream.Readable
26+
if (file.tempFilePath) {
27+
fileBufferOrStream = fs.createReadStream(file.tempFilePath)
28+
} else {
29+
fileBufferOrStream = file.buffer
30+
}
31+
2132
await getStorageClient().putObject({
2233
Bucket: bucket,
23-
Key: path.posix.join(prefix, file.filename),
24-
Body: file.buffer,
34+
Key: fileKey,
35+
Body: fileBufferOrStream,
2536
ACL: acl,
2637
ContentType: file.mimeType,
2738
})

src/adapters/s3/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export const s3Adapter =
1818
let storageClient: AWS.S3 | null = null
1919
const getStorageClient = () => {
2020
if (storageClient) return storageClient
21-
return (storageClient = new AWS.S3(config))
21+
storageClient = new AWS.S3(config)
22+
return storageClient
2223
}
2324

2425
return {

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import type { Configuration as WebpackConfig } from 'webpack'
77
export interface File {
88
buffer: Buffer
99
filename: string
10+
filesize: number
1011
mimeType: string
12+
tempFilePath?: string
1113
}
1214

1315
export type HandleUpload = (args: {

src/utilities/getIncomingFiles.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export function getIncomingFiles({
1818
filename: data.filename,
1919
mimeType: data.mimeType,
2020
buffer: file.data,
21+
tempFilePath: file.tempFilePath,
22+
filesize: file.size,
2123
}
2224

2325
files = [mainFile]
@@ -30,6 +32,7 @@ export function getIncomingFiles({
3032
filename: `${resizedFileData.filename}`,
3133
mimeType: data.mimeType,
3234
buffer: req.payloadUploadSizes[key],
35+
filesize: req.payloadUploadSizes[key].length,
3336
},
3437
])
3538
}

0 commit comments

Comments
 (0)