aws s3 与 google cloud storage js方法

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),我们需要对以下方面进行修改:

  1. 使用 @google-cloud/storage 库,而不是 AWS 的 S3 客户端。
  2. 调整上传、复制和检查文件存在的方法,以适应 GCS 的 API。
  3. 移除与 CloudFront 相关的代码(如果需要使用 GCS 的相关功能,比如 Cloud CDN,则需要添加相应的代码)。

下面是更改后的代码示例。请注意,这段代码假设你已经设置了 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 兼容。
  • 删除了与 AWS S3 或 CloudFront 相关的函数和调用。
  • 添加了 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) 项目中的资源进行交互。下面是如何获取这些值的步骤:

  1. projectId:

    • 这是你在 Google Cloud Platform 创建项目时分配给该项目的唯一标识符。
    • 你可以在 GCP 控制台的“首页”或者“项目信息”部分找到项目 ID。
  2. keyFilename:

    • 这是指向下载的服务账号密钥文件的路径。服务账号允许应用程序以非交互方式对 GCP API 进行身份验证和授权。
    • 要创建服务账号并下载密钥文件,你需要执行以下操作:
      1. 登录到 Google Cloud Console。
      2. 打开导航菜单,进入“IAM & 管理” > “服务账号”。
      3. 点击“创建服务账号”,填写必要的信息后点击“创建”。
      4. 在“授予此服务帐号访问项目”阶段,选择合适的角色(例如Storage Object Admin,如果需要完整权限)。
      5. 点击“继绀”,然后点击“完成”完成服务账号的创建。
      6. 在服务账号列表中,找到你刚才创建的账号,点击“操作”列的“管理密钥”。
      7. 点击“添加密钥” > “创建新密钥”,选择 JSON 类型,然后点击“创建”。密钥文件将被下载到你的计算机。
    • 下载完成后,你将得到一个 .json 文件,其中包含了你的私钥信息和其他重要数据。keyFilename 应该指向这个文件的位置。
  3. bucketName:

    • 这是你在 GCP 中创建的 Cloud Storage 存储桶的名称。
    • 你可以通过以下步骤来创建或者查看现有存储桶:
      1. 登录到 Google Cloud Console。
      2. 打开导航菜单,进入“Cloud Storage” > “浏览器”。
      3. 在这里,你可以看到所有的存储桶或者通过点击“创建存储桶”来创建新的存储桶。
    • 存储桶名称全球唯一,并且一旦创建不能更改。

确保你的应用程序可以访问到服务账号密钥文件的路径,并在代码中正确设置这些参数。安全起见,请不要将服务账号密钥文件公开或上传到版本控制系统中。

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,以下是几种不同的指定方式:

  1. 使用绝对路径(推荐):

    const storage = new Storage({
        projectId: 'your-google-cloud-project-id',
        keyFilename: '/absolute/path/to/service-account-key.json'
    });
    
  2. 使用相对路径,例如,如果密钥文件位于运行代码的当前工作目录下的子目录中:

    const storage = new Storage({
        projectId: 'your-google-cloud-project-id',
        keyFilename: './subdirectory/service-account-key.json'
    });
    
  3. 如果密钥文件和执行脚本位于同一目录中,可以直接使用文件名:

    const storage = new Storage({
        projectId: 'your-google-cloud-project-id',
        keyFilename: 'service-account-key.json'
    });
    

确保应用程序运行时具有访问指定路径中密钥文件的权限。通常建议使用绝对路径,因为这样可以避免依赖于当前工作目录,使配置更加明确和稳定。