求助:修复基于googleapis的Google Drive分片上传文件不完整问题
Google Drive 分片上传问题排查与修复方案
原代码核心问题
- 可恢复上传初始化错误:
uploadType参数应放在请求根参数中,而非requestBody内- 初始化可恢复上传时,Google API返回的是可恢复上传URL,而非直接生成fileId,原代码错误地提前获取
res.data.id,此时文件并未真正创建
- 分片上传方式错误:
不应调用drive.files.update方法,需使用初始化得到的可恢复URL直接发送PUT请求传输分片
修复后的完整代码
const fs = require("fs"); const { google } = require("googleapis"); const axios = require("axios"); // 需安装axios:npm install axios const { getGoogleClient } = require("../../../utils/google"); async function uploadFile(file) { const auth = getGoogleClient(); const drive = google.drive({ auth, version: "v3", }); const folderId = process.env.GOOGLE_FOLDER_ID; const chunkSize = 1024 * 1024; // 1MB分片 const filePath = file.path; const fileName = file.name; const fileSize = fs.statSync(filePath).size; if (fileSize <= chunkSize) { // 小文件直接上传 const media = { mimeType: file.type, body: fs.createReadStream(filePath), }; await drive.files.create({ requestBody: { name: fileName, parents: [folderId] }, media, supportsAllDrives: true, }); } else { // 初始化可恢复上传 const initResponse = await drive.files.create({ requestBody: { name: fileName, parents: [folderId] }, uploadType: "resumable", // 正确放置uploadType参数 supportsAllDrives: true, headers: { "X-Upload-Content-Type": file.type, // 使用文件实际MIME类型 "X-Upload-Content-Length": fileSize.toString(), }, }); // 获取可恢复上传URL const resumableUrl = initResponse.headers["location"]; if (!resumableUrl) { throw new Error("Failed to get resumable upload URL"); } let start = 0; let chunkIndex = 0; while (start < fileSize) { const end = Math.min(start + chunkSize, fileSize); const chunkLength = end - start; // 创建分片读取流(fs的end参数是排他的,需减1) const chunkStream = fs.createReadStream(filePath, { start, end: end - 1, }); // 准备请求头 const headers = { "Content-Type": file.type, "Content-Range": `bytes ${start}-${end - 1}/${fileSize}`, "Content-Length": chunkLength.toString(), }; // 发送分片上传请求 const uploadResponse = await axios.put(resumableUrl, chunkStream, { headers, auth: { bearer: await auth.getAccessToken(), // 手动携带access token }, }); // 处理上传状态 if (uploadResponse.status === 200) { console.log("File uploaded successfully:", uploadResponse.data.id); break; } else if (uploadResponse.status === 308) { // 308表示需继续上传,解析已完成的范围 const range = uploadResponse.headers["range"]; start = range ? parseInt(range.split("-")[1]) + 1 : end; } console.log(`Chunk ${chunkIndex} uploaded, next start at ${start}`); chunkIndex++; } } console.log("Done!", fileName); }
关键修改说明
- 初始化流程:修正
uploadType参数位置,正确获取可恢复上传URL - 分片传输:改用axios直接请求resumable URL,适配Google Drive的可恢复上传协议
- 断点续传支持:处理308状态码,从已上传位置继续传输
- 参数统一:使用文件实际MIME类型,修正分片读取范围的边界问题
内容的提问来源于stack exchange,提问作者Kombat
相关产品推荐
相关产品推荐

