You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自动重命名Google云存储签名下载链接的文件?

问题:Google云存储签名下载链接自动重命名方案

我通过外部API获取Google云存储的签名直接下载链接,点击可自动下载文件,但文件名是默认值(如123456123456.csv.gz)。用Puppeteer打开链接下载无法自动重命名,修改URL里的文件名会触发SignatureDoesNotMatch错误,用axios/fs直接下载也没成功,求自动重命名的可行方案。

链接格式示例

https://storage.googleapis.com/(custom storage name)/(data extract id)/123456123456.csv.gz?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=(google service account credentials)&X-Goog-Date=(date)&X-Goog-Expires=(time until expiration)&X-Goog-SignedHeaders=host&X-Goog-Signature=(long string of number and letters)

当前Puppeteer代码

async function downloadFile (url) {
  try {
    const browser = await puppeteer.launch(
      {headless: false}
    );
    const page = await browser.newPage();
    await page.goto(`${url}`);
    await page.waitForTimeout(3000); // 防止下载延迟
    await browser.close();
  }
  catch (error) {
    console.error(error);
  }
}

修改URL的尝试代码

...
  const autoFileName = '000000000000.csv.gz';
  const customFileName = `${date}-${reportingLevel}.csv.gz`;
  for (let i = 0; i < urls.length; i++) {
    const newUrl = url.replace(autoFileName, customFileName);
    downloadFile(newUrl);
...

报错信息

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>Access denied.</Message>
<Details>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Details>
<StringToSign>(sensitive information)</StringToSign>
<CanonicalRequest>GET (sensitive information) UNSIGNED-PAYLOAD</CanonicalRequest>
</Error>

可行解决方案

方案1:通过Puppeteer拦截下载并指定文件名

Google云存储的签名链接包含完整路径的签名,不能直接修改URL中的文件名,但可以通过Puppeteer的下载拦截功能,直接指定保存的文件名。

代码示例:

async function downloadFile(url, customFileName) {
  try {
    const browser = await puppeteer.launch({ headless: false });
    const page = await browser.newPage();

    // 配置下载行为,指定保存目录和文件名
    await page.setDownloadBehavior({
      path: './downloads/', // 需提前创建该目录
      filename: customFileName
    });

    await page.goto(url);
    // 监听下载完成事件,替代固定等待时间
    page.on('download', async (download) => {
      await download.saveAs(`./downloads/${customFileName}`);
      await browser.close();
    });
  } catch (error) {
    console.error(error);
  }
}

// 调用示例
const date = new Date().toISOString().split('T')[0];
const reportingLevel = 'summary';
const customFileName = `${date}-${reportingLevel}.csv.gz`;
downloadFile(yourSignedUrl, customFileName);

方案2:用axios流式下载后重命名

之前用axios/fs失败大概率是没处理流式响应,签名链接支持直接流式下载,下载完成后再重命名即可。

代码示例:

const axios = require('axios');
const fs = require('fs');
const path = require('path');

async function downloadAndRename(url, customFileName) {
  try {
    const response = await axios({
      url: url,
      method: 'GET',
      responseType: 'stream' // 启用流式下载,避免大文件占用内存
    });

    const tempPath = path.join('./downloads/', 'temp_download.tmp');
    const writer = fs.createWriteStream(tempPath);

    response.data.pipe(writer);

    // 等待下载完成
    await new Promise((resolve, reject) => {
      writer.on('finish', resolve);
      writer.on('error', reject);
    });

    // 重命名为目标文件名
    const finalPath = path.join('./downloads/', customFileName);
    fs.renameSync(tempPath, finalPath);
    console.log(`文件已保存:${finalPath}`);
  } catch (error) {
    console.error('下载失败:', error);
  }
}

// 调用示例
downloadAndRename(yourSignedUrl, `${date}-${reportingLevel}.csv.gz`);

方案3:通过Google云存储SDK下载(需权限)

如果拥有对应GCS服务账号权限,可以直接通过官方SDK获取文件并重命名,适合长期集成场景。

代码示例:

const { Storage } = require('@google-cloud/storage');
const fs = require('fs');

async function downloadFromGcs(bucketName, filePath, customFileName) {
  const storage = new Storage({
    credentials: require('./service-account-key.json') // 你的服务账号密钥文件
  });

  const bucket = storage.bucket(bucketName);
  const file = bucket.file(filePath);

  await file.download({
    destination: `./downloads/${customFileName}`
  });
  console.log(`文件已保存:./downloads/${customFileName}`);
}

// 从签名链接中提取bucket和文件路径
const urlPath = yourSignedUrl.split('?')[0].split('/');
const bucketName = urlPath[3];
const filePath = urlPath.slice(4).join('/');

downloadFromGcs(bucketName, filePath, `${date}-${reportingLevel}.csv.gz`);

内容的提问来源于stack exchange,提问作者courtneymickelsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 08:49:59