Play Cloudflare
Written by Eddie ZhangatJanuary 12, 2025
cloudflare
r2
snippets
table of contents
I use Cloudflare R2 to store my blog data
create client
typescriptconst R2Storage = new S3Client({ region: 'auto', endpoint: `https://${CLOUDFLARE_R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: CLOUDFLARE_R2_ACCESS_KEY_ID, secretAccessKey: CLOUDFLARE_R2_SECRET_ACCESS_KEY, }, });
this is a aws sdk bug, because aws sdk tries to create a bucket if it doesn't exist, even though it is not allowed to do so.
basic operations
list buckets
typescriptexport const listBuckets = async () => { return await R2Storage.send(new ListBucketsCommand({})); };
list objects in bucket
typescriptexport const listObjectsInBucket = async ( bucketName: string ) => { return await R2Storage.send(new ListObjectsV2Command({ Bucket: bucketName })); };
get object
typescriptconst getObject = async ( key: string, bucketName: string ) => { return await R2Storage.send( new GetObjectCommand({ Bucket: bucketName, Key: key }) ); };
has object in bucket
typescriptexport const hasObjectInBucket = async ( key: string, bucketName: string ): Promise<boolean> => { try { await R2Storage.send( new GetObjectCommand({ Bucket: bucketName, Key: key }) ); return true; } catch (error) { return false; } };
delete object
typescriptexport const deleteObject = async ( key: string, bucketName: string ) => { await R2Storage.send( new DeleteObjectCommand({ Bucket: bucketName, Key: key, }) ); };
update object
typescriptexport const updateObject = async ( filePath: string, key: string, checkIfExists: boolean = true, bucketName: string ) => { // I defined checkIfExists because sometimes we are very sure that an object is in bucket or not. const exists = checkIfExists ? await hasObjectInBucket(key, bucketName) : true; if (exists) { await deleteObject(key, bucketName); } return await uploadObject(filePath, key, bucketName); };
upload object
typescriptexport const uploadObject = async ( filePath: string, key: string = path.basename(filePath), bucketName: string ) => { //have to set content type, otherwise images can not be read const contentType = mime.lookup(filePath) || 'application/octet-stream'; return await R2Storage.send( new PutObjectCommand({ Bucket: bucketName, Key: key, Body: await fs.promises.readFile(filePath), ContentType: contentType, }) ); };
download object
typescriptexport const downloadObject = async ( key: string, intoDirName: string = 'public', bucketName: string ) => { const intoDir = process.cwd() + '/' + intoDirName; const object = await getObject(key, bucketName); // Create the full directory path including subdirectories const fullPath = path.join(intoDir, key); const targetDir = path.dirname(fullPath); await fs.promises.mkdir(targetDir, { recursive: true }); // Convert the readable stream to a buffer and write to file /*eslint-disable @typescript-eslint/no-explicit-any*/ const chunks: any[] = []; for await (const chunk of object.Body as any) { chunks.push(chunk); } /*eslint-enable @typescript-eslint/no-explicit-any*/ const buffer = Buffer.concat(chunks); await fs.promises.writeFile(fullPath, buffer); return fullPath; };
download folder
typescriptexport const downloadFolder = async ( prefix: string, intoDirName: string = 'public', bucketName: string ) => { const response = await R2Storage.send( new ListObjectsV2Command({ Bucket: bucketName, Prefix: prefix, }) ); if (!response.Contents) { return []; } // Download each object const downloads = response.Contents.map((object) => { if (!object.Key) return; return downloadObject(object.Key, intoDirName, bucketName); }).filter(Boolean); // Wait for all downloads to complete const downloadedPaths = await Promise.all(downloads); return downloadedPaths; };
upload folder
typescriptexport const uploadFolder = async ( folderPath: string, prefix: string = '', bucketName: string ) => { const files = await fs.promises.readdir(folderPath, { withFileTypes: true }); const uploads = []; for (const file of files) { const fullPath = path.join(folderPath, file.name); const key = path.join(prefix, file.name).replace(/\\/g, '/'); if (file.isDirectory()) { uploads.push(uploadFolder(fullPath, key, bucketName)); } else { uploads.push(uploadObject(fullPath, key, bucketName)); } } await Promise.all(uploads); return true; };