import { AWS, ASSET_BASE_URL } from '../constants'; import { md5 } from '../utils/crypto'; import { S3Client, CopyObjectCommand, PutObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3'; import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront'; const { Upload } = require('@aws-sdk/lib-storage');
const stream = require('stream');
const s3Client = new S3Client({ region: AWS.region, credentials: AWS.credentials });
const cfClient = new CloudFrontClient({ region: AWS.region, credentials: AWS.credentials });
export function parseUrl(url: string) { let i = url.indexOf('/'); return { host: url.substring(0, i), path: i == -1 ? '' : url.substring(i + 1) }; }
export async function s3Copy(sourcePath: any, targetPath: any) { if (targetPath.substring(0, 1) === '/') { targetPath = targetPath.substring(1); } const bucketParams = { Bucket: AWS.bucket, CopySource: AWS.bucket + sourcePath, Key: targetPath }; console.debug('s3Copy Params:', bucketParams);
try {
const data = await s3Client.send(new CopyObjectCommand(bucketParams));
console.debug('Success', data);
return data;
} catch (e) {
console.error('s3Copy Except:', e);
throw e;
}
}
/**
s3 upload file
@param body, fileStream | string
@param targetPath string
@returns
*/
export async function s3Upload(targetPath: any, body: any, contentType?: string) {
if (targetPath.substring(0, 1) === '/') {
targetPath = targetPath.substring(1);
}
const uploadParams: any = {
Bucket: AWS.bucket,
Key: targetPath,
Body: body
};
if (contentType) {
if (contentType === 'text/plain') {
uploadParams.ContentType = ${contentType}; charset=utf-8
;
} else {
uploadParams.ContentType = contentType;
}
}
try { const data = await s3Client.send(new PutObjectCommand(uploadParams)); console.debug('Success', data); return data; } catch (e: any) { console.error('Error', e.message); throw e; } }
/**
s3 stream upload file
@param body, fileStream | string
@param targetPath string
@returns */ export async function s3UploadStream(targetPath: any, body: any, contentType?: string) { if (targetPath.substring(0, 1) === '/') { targetPath = targetPath.substring(1); }
const passThrough = new stream.PassThrough(); passThrough.end(body);
const uploadParams: any = { Bucket: AWS.bucket, Key: targetPath, Body: passThrough };
if (contentType) {
if (contentType === 'text/plain') {
uploadParams.ContentType = ${contentType}; charset=utf-8
;
} else {
uploadParams.ContentType = contentType;
}
}
try { const parallelUploads3 = new Upload({ client: s3Client, params: uploadParams, queueSize: 20, // 并发上传的数量 partSize: 5 * 1024 * 1024, // 每个分块的大小 leavePartsOnError: false // 如果出错是否保留已上传的分块 });
// parallelUploads3.on('httpUploadProgress', (progress: any) => {
// console.log('progress:::', progress);
// });
const data = await parallelUploads3.done();
// console.debug('Success', data);
return data;
} catch (e: any) { console.error('Error', e.message); throw e; } }
export async function cfCreateInvalidation(path: any) { const input: any = { DistributionId: AWS.distributionId, InvalidationBatch: { CallerReference: new Date().getTime(), Paths: { Items: Array.isArray(path) ? path : [path], Quantity: 1 } } }; // console.debug('cfCreateInvalidation param:', input);
try {
const command = new CreateInvalidationCommand(input);
const response = await cfClient.send(command);
// console.debug('Success', response);
return response;
} catch (e: any) {
console.error('cfCreateInvalidation Except:', e.message);
throw e;
}
}
export async function s3FileExists(path: string) { try { const command = new HeadObjectCommand({ Bucket: AWS.bucket, Key: path }); await s3Client.send(command); return true; } catch (error: any) { if (error?.name === 'NotFound') { return false; } throw error; } }
export async function uploadByFormData(file: any, folder: string, name?: string) { if (!file || !file?.name || !file?.data || !file?.mimetype) { throw new Error('upload: invalid parameter'); } let fileExt = ''; if (file.name) { fileExt = file.name.substring(file.name.lastIndexOf('.')); } if (!name) { name = md5(file.data); } const targetPath = folder + name + fileExt; // console.debug('path ', targetPath);
// await s3Upload(targetPath, file.data, file.mimetype);
await s3UploadStream(targetPath, file.data, file.mimetype);
await cfCreateInvalidation(targetPath);
return ASSET_BASE_URL + targetPath;
} 请帮我把上面typescript s3操作方法 改成 google cloud storage操作方法
为了将上述 TypeScript 代码从 AWS S3 操作更改为使用 Google Cloud Storage (GCS),我们需要对以下方面进行修改:
@google-cloud/storage
库,而不是 AWS 的 S3 客户端。下面是更改后的代码示例。请注意,这段代码假设你已经设置了 GCS 的认证,并且你有一个有效的 GCS 存储桶和可选的 Cloud CDN 配置。
import { Storage } from '@google-cloud/storage';
import path from 'path';
const storage = new Storage({
projectId: 'your-google-cloud-project-id',
keyFilename: 'path-to-your-service-account-file.json'
});
const bucketName = 'your-bucket-name';
const bucket = storage.bucket(bucketName);
export function parseUrl(url: string) {
const { host, pathname } = new URL(url);
return {
host,
path: pathname.substring(1)
};
}
export async function gsCopy(sourcePath: string, targetPath: string) {
if (targetPath.startsWith('/')) {
targetPath = targetPath.substring(1);
}
try {
await bucket.file(sourcePath).copy(bucket.file(targetPath));
console.debug('Success');
} catch (e) {
console.error('gsCopy Except:', e);
throw e;
}
}
export async function gsUpload(targetPath: string, body: Buffer | ReadableStream | Blob | string, contentType?: string) {
if (targetPath.startsWith('/')) {
targetPath = targetPath.substring(1);
}
const options: any = {
resumable: false,
metadata: {}
};
if (contentType) {
options.metadata.contentType = contentType;
}
try {
await bucket.file(targetPath).save(body, options);
console.debug('Success');
} catch (e) {
console.error('Error', e.message);
throw e;
}
}
export async function gsFileExists(path: string) {
try {
const [exists] = await bucket.file(path).exists();
return exists;
} catch (error) {
console.error('gsFileExists Except:', error.message);
throw error;
}
}
export async function uploadByFormData(file: any, folder: string, name?: string) {
if (!file || !file.name || !file.data || !file.mimetype) {
throw new Error('upload: invalid parameter');
}
let fileExt = '';
if (file.name) {
fileExt = path.extname(file.name);
}
if (!name) {
name = md5(file.data);
}
const targetPath = path.join(folder, name + fileExt);
await gsUpload(targetPath, file.data, file.mimetype);
// If you have a CDN, create an invalidation here as necessary.
const publicUrl = `https://storage.googleapis.com/${bucketName}/${encodeURIComponent(targetPath)}`;
return publicUrl;
}
该代码片段包含以下几个主要的更改点:
@google-cloud/storage
库。gsUpload
) 和复制 (gsCopy
) 函数,使其与 GCS API 兼容。uploadByFormData
函数,用于处理表单数据的上传到 GCS。注意:上述代码未包含任何 CDN 的无效化处理。如果你使用了例如 Google Cloud CDN 等服务,你可能需要通过 Google Cloud Platform 的相关 API 来添加缓存无效化的逻辑。
在实际部署前,请确保替换所有必要的占位符(例如 'your-google-cloud-project-id'
, 'path-to-your-service-account-file.json'
和 'your-bucket-name'
),并根据你的具体需求进行适当的调整。
const storage = new Storage({ projectId: 'your-google-cloud-project-id', keyFilename: 'path-to-your-service-account-file.json' });
const bucketName = 'your-bucket-name'; 这几个参数值从哪里来?
这些参数值是用于配置和认证 Google Cloud Storage 客户端库的关键信息,以便它可以与你的 Google Cloud Platform (GCP) 项目中的资源进行交互。下面是如何获取这些值的步骤:
projectId
:
keyFilename
:
Storage Object Admin
,如果需要完整权限)。.json
文件,其中包含了你的私钥信息和其他重要数据。keyFilename
应该指向这个文件的位置。bucketName
:
确保你的应用程序可以访问到服务账号密钥文件的路径,并在代码中正确设置这些参数。安全起见,请不要将服务账号密钥文件公开或上传到版本控制系统中。
const storage = new Storage({ projectId: 'your-google-cloud-project-id', keyFilename: 'path-to-your-service-account-file.json' }); 这里keyFilename 是绝对路径,还是必须放到项目根目录的文件名?
在 Storage
类构造函数中提供的 keyFilename
参数可以是服务账号密钥文件的绝对路径,也可以是相对于当前工作目录的相对路径。它不必放在项目的根目录中。
例如,如果你的服务账号密钥文件名为 service-account-key.json
,以下是几种不同的指定方式:
使用绝对路径(推荐):
const storage = new Storage({
projectId: 'your-google-cloud-project-id',
keyFilename: '/absolute/path/to/service-account-key.json'
});
使用相对路径,例如,如果密钥文件位于运行代码的当前工作目录下的子目录中:
const storage = new Storage({
projectId: 'your-google-cloud-project-id',
keyFilename: './subdirectory/service-account-key.json'
});
如果密钥文件和执行脚本位于同一目录中,可以直接使用文件名:
const storage = new Storage({
projectId: 'your-google-cloud-project-id',
keyFilename: 'service-account-key.json'
});
确保应用程序运行时具有访问指定路径中密钥文件的权限。通常建议使用绝对路径,因为这样可以避免依赖于当前工作目录,使配置更加明确和稳定。