基于Node.js调用Google Drive API下载图片失败求助
解决Google Drive API下载图片后无法查看的问题
嘿,我看你刚接触API开发,在用Node.js调用Google Drive API下载图片时遇到了下载成功但图片打不开的问题——这其实是流处理的常见小坑,咱们来一步步解决它:
核心问题分析
你的代码里存在几个关键细节没处理好,导致图片文件不完整或损坏:
- 流写入未确认完成:你在
pipe(dest)后直接打印"success",但Node.js的文件写入流是异步的,此时数据可能还没完全写入磁盘 - 重复下载覆盖文件:在
listFiles里循环调用downloadFile,但downloadFile用的是固定fileId,会导致同一个文件被重复下载覆盖,可能损坏文件 - 授权函数参数冗余:调用
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
相关产品推荐
相关产品推荐

