UNPKG

@rytass/storages-adapter-r2

Version:

Cloudflare R2 storage adapter

159 lines (155 loc) 5.07 kB
'use strict'; var storages = require('@rytass/storages'); var awsSdk = require('aws-sdk'); var stream = require('stream'); var uuid = require('uuid'); class StorageR2Service extends storages.Storage { bucket; s3; parseSignedURL; constructor(options){ super(options); this.bucket = options.bucket; if (options.customDomain) { const re = new RegExp(`^https://${options.bucket}.${options.account}.r2.cloudflarestorage.com`); this.parseSignedURL = (url)=>{ return url.replace(re, options.customDomain); }; } this.s3 = new awsSdk.S3({ endpoint: `https://${options.account}.r2.cloudflarestorage.com`, credentials: new awsSdk.Credentials({ accessKeyId: options.accessKey, secretAccessKey: options.secretKey }), region: 'auto', signatureVersion: 'v4' }); } async url(key, options) { const signedURL = await this.s3.getSignedUrlPromise('getObject', { Bucket: this.bucket, Key: key, ...options?.expires ? { Expires: options?.expires } : {} }); if (this.parseSignedURL) { return this.parseSignedURL(signedURL); } return signedURL; } async read(key, options) { try { const response = await this.s3.getObject({ Bucket: this.bucket, Key: key }).promise(); if (options?.format === 'buffer') { return response.Body; } return stream.Readable.from(response.Body); } catch (ex) { if (ex && typeof ex === 'object' && 'name' in ex && ex.name === 'NoSuchKey') { throw new storages.StorageError(storages.ErrorCode.READ_FILE_ERROR, 'File not found'); } throw ex; } } async writeStreamFile(stream$1, options) { const givenFilename = options?.filename; if (givenFilename) { await this.s3.upload({ Bucket: this.bucket, Key: givenFilename, Body: stream$1, ...options?.contentType ? { ContentType: options?.contentType } : {} }).promise(); return { key: givenFilename }; } const tempFilename = uuid.v4(); const uploadStream = new stream.PassThrough(); const getFilenamePromise = this.getStreamFilename(stream$1); const uploadPromise = this.s3.upload({ Bucket: this.bucket, Key: tempFilename, Body: uploadStream, ...options?.contentType ? { ContentType: options?.contentType } : {} }).promise(); stream$1.pipe(uploadStream); const [[filename, mime]] = await Promise.all([ getFilenamePromise, uploadPromise ]); await this.s3.copyObject({ Bucket: this.bucket, CopySource: `/${this.bucket}/${tempFilename}`, Key: filename, ...mime ? { ContentType: mime } : {}, ...options?.contentType ? { ContentType: options?.contentType } : {} }).promise(); await this.s3.deleteObject({ Bucket: this.bucket, Key: tempFilename }).promise(); return { key: filename }; } async writeBufferFile(buffer, options) { const fileInfo = options?.filename || await this.getBufferFilename(buffer); const filename = Array.isArray(fileInfo) ? fileInfo[0] : fileInfo; await this.s3.upload({ Key: filename, Bucket: this.bucket, Body: buffer, ...Array.isArray(fileInfo) && fileInfo[1] ? { ContentType: fileInfo[1] } : {}, ...options?.contentType ? { ContentType: options?.contentType } : {} }).promise(); return { key: filename }; } write(file, options) { if (file instanceof Buffer) { return this.writeBufferFile(file, options); } return this.writeStreamFile(file, options); } batchWrite(files) { return Promise.all(files.map((file)=>this.write(file))); } async remove(key) { await this.s3.deleteObject({ Bucket: this.bucket, Key: key }).promise(); } async isExists(key) { try { await this.s3.headObject({ Bucket: this.bucket, Key: key }).promise(); return true; } catch (ex) { if (ex && typeof ex === 'object' && 'name' in ex && ex.name === 'NotFound') return false; throw ex; } } } exports.StorageR2Service = StorageR2Service;