从URL上传文件至Google Cloud Storage遇报错,求协助解决
问题分析
你遇到的核心问题是:Google Cloud Storage的bucket.upload()方法只接受本地文件系统的路径作为参数,但你传入的是一个远程HTTP URL。Node.js会把这个URL解析成本地路径去查找,所以才会抛出ENOENT: no such file or directory的错误。另外你的代码没有处理异步错误,导致出现了未处理的Promise拒绝警告。
解决方案
要上传远程URL的文件到GCS,我们需要先获取远程文件的内容,再上传到GCS。这里推荐两种方法,优先选择流式上传(无需本地存储):
方法一:流式上传(推荐)
直接通过流的方式把远程文件传输到GCS,不需要在本地保存文件,更高效省空间。我们可以用axios获取远程文件流,再通过GCS的写入流上传:
首先安装依赖(如果没装的话):
npm install axios
修改后的代码:
const axios = require('axios'); const { PassThrough } = require('stream'); app.post("/uploads", upload.any("tmparr"), async (req, res) => { try { // 远程文件URL const remoteFileUrl = "https://lalalai.s3-us-west-2.amazonaws.com/media/split/9f6f7cb6-43b2-45ee-bec7-16ae2f7214b5/vocals"; // 获取远程文件流 const response = await axios.get(remoteFileUrl, { responseType: 'stream' }); // 创建一个中转流(处理流的管道传递) const passThroughStream = new PassThrough(); response.data.pipe(passThroughStream); // 指定GCS中的目标文件名 const targetGcsFile = storage.bucket(bucketName).file('your-target-filename'); // 通过流上传到GCS await new Promise((resolve, reject) => { passThroughStream .pipe(targetGcsFile.createWriteStream({ gzip: true, metadata: { cacheControl: "public, max-age=31536000", }, })) .on('finish', resolve) // 上传完成触发 .on('error', reject); // 上传失败触发 }); console.log("文件成功上传到GCS"); res.status(200).send("上传完成"); } catch (error) { console.error("上传出错:", error); res.status(500).send("上传失败"); } });
方法二:先下载到本地临时文件再上传
如果需要临时保存文件到本地,可以先把远程文件下载到临时目录,上传完成后再删除临时文件:
const axios = require('axios'); const fs = require('fs').promises; const path = require('path'); const os = require('os'); app.post("/uploads", upload.any("tmparr"), async (req, res) => { let tempFilePath; try { const remoteFileUrl = "https://lalalai.s3-us-west-2.amazonaws.com/media/split/9f6f7cb6-43b2-45ee-bec7-16ae2f7214b5/vocals"; // 创建本地临时文件路径 tempFilePath = path.join(os.tmpdir(), `temp-vocals-${Date.now()}`); // 下载远程文件到本地临时文件 const response = await axios.get(remoteFileUrl, { responseType: 'stream' }); await new Promise((resolve, reject) => { const writeStream = fs.createWriteStream(tempFilePath); response.data.pipe(writeStream); writeStream.on('finish', resolve); writeStream.on('error', reject); }); // 上传临时文件到GCS await storage.bucket(bucketName).upload(tempFilePath, { gzip: true, metadata: { cacheControl: "public, max-age=31536000", }, destination: 'your-target-filename' // GCS中的目标文件名 }); console.log("done"); res.status(200).send("上传成功"); } catch (error) { console.error("错误:", error); res.status(500).send("上传失败"); } finally { // 清理临时文件,避免占用磁盘空间 if (tempFilePath) { try { await fs.unlink(tempFilePath); } catch (cleanupError) { console.error("删除临时文件失败:", cleanupError); } } } });
关键注意事项
- 一定要用
try/catch包裹异步代码,处理可能出现的错误,避免未处理的Promise拒绝(这也是你原来代码出现警告的原因) - 流式上传不需要本地磁盘空间,性能更好,优先使用
- 如果远程文件需要权限访问(比如私有S3文件),需要在请求中添加对应的认证头(比如AWS签名)
内容的提问来源于stack exchange,提问作者ubi goreng
相关产品推荐
相关产品推荐

