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

基于Node.js调用Google Drive API下载图片失败求助

解决Google Drive API下载图片后无法查看的问题

嘿,我看你刚接触API开发,在用Node.js调用Google Drive API下载图片时遇到了下载成功但图片打不开的问题——这其实是流处理的常见小坑,咱们来一步步解决它:

核心问题分析

你的代码里存在几个关键细节没处理好,导致图片文件不完整或损坏:

  1. 流写入未确认完成:你在pipe(dest)后直接打印"success",但Node.js的文件写入流是异步的,此时数据可能还没完全写入磁盘
  2. 重复下载覆盖文件:在listFiles里循环调用downloadFile,但downloadFile用的是固定fileId,会导致同一个文件被重复下载覆盖,可能损坏文件
  3. 授权函数参数冗余:调用authorize时传了三个参数,但函数只接受两个,虽然不影响下载,但逻辑上不够严谨

修改后的完整代码方案

1. 修复下载函数的流处理逻辑

const express = require("express");
const path = require ("path");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const app = express();
app.use(express.static(path.join(__dirname, 'public')));

// Google getting started section
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');

// 确保图片目录存在,避免写入失败
const imageDir = path.join(__dirname, 'public', 'images');
if (!fs.existsSync(imageDir)) {
  fs.mkdirSync(imageDir, { recursive: true });
}

// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.readonly', 'https://www.googleapis.com/auth/drive.metadata.readonly'];
const TOKEN_PATH = 'token.json';

// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
  if (err) return console.log('Error loading client secret file:', err);
  // 修正授权调用,只传必要的回调函数
  authorize(JSON.parse(content), listFiles);
});

function authorize(credentials, callback) {
  const {client_secret, client_id, redirect_uris} = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
      client_id, client_secret, redirect_uris[0]);

  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) return getAccessToken(oAuth2Client, callback);
    oAuth2Client.setCredentials(JSON.parse(token));
    callback(oAuth2Client);
  });
}

function getAccessToken(oAuth2Client, callback) {
  const authUrl = oAuth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question('Enter the code from that page here: ', (code) => {
    rl.close();
    oAuth2Client.getToken(code, (err, token) => {
      if (err) return console.error('Error retrieving access token', err);
      oAuth2Client.setCredentials(token);
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) return console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}

function listFiles(auth) {
  const drive = google.drive({version: 'v3', auth});
  drive.files.list({
    pageSize: 10,
    fields: 'nextPageToken, files(id, name, mimeType)', // 新增mimeType字段,过滤图片文件
  }, (err, res) => {
    if (err) return console.log('The API returned an error: ' + err);
    const files = res.data.files;
    if (files.length) {
      console.log('Files:');
      files.map((file) => {
        console.log(`${file.name} (${file.id}) - ${file.mimeType}`);
        // 只下载图片类型的文件
        if (file.mimeType.startsWith('image/')) {
          downloadFile(auth, file.id, file.name);
        }
      });
    } else {
      console.log('No files found.');
    }
  });
}

// 优化下载函数,支持动态传入文件ID和名称
function downloadFile(auth, fileId, fileName) {
const drive = google.drive({version: "v3", auth});
var dest = fs.createWriteStream(`public/images/${fileName}`);
drive.files.get({fileId: fileId, alt: "media"}, {responseType: "stream"}, function(err, res){
  if (err) {
    console.log("Error downloading file:", err);
    return;
  }
  res.data
    .on("end", () => {
      console.log(`Download stream for ${fileName} ended`);
    })
    .on("error", err => {
      console.log("Error during download:", err);
    })
    .pipe(dest);
  
  // 监听写入流的finish事件,确认文件完全写入磁盘后再提示成功
  dest.on("finish", () => {
    console.log(`✅ ${fileName} downloaded successfully!`);
  });

  // 捕获写入磁盘时的错误
  dest.on("error", (err) => {
    console.log("Error writing file to disk:", err);
  });
});
}

关键修改说明

  • 确保图片目录存在:新增了目录检查和创建逻辑,避免因目录不存在导致写入失败
  • 动态传入文件信息:修改downloadFile支持传入文件ID和名称,避免固定值导致重复覆盖
  • 监听写入完成事件:通过dest.on("finish")确认文件完全写入后再提示成功,解决文件不完整问题
  • 过滤图片文件:通过mimeType.startsWith('image/')只下载图片类型的文件,避免下载非图片内容
  • 修正授权调用:移除冗余的参数,让授权逻辑更清晰

后续页面渲染建议

下载完成后,你可以在EJS模板里直接通过静态文件路径引用图片,比如:

<img src="/images/[你的图片文件名]" alt="Google Drive图片">

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 06:42:32