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

Google Drive API+Spring Boot+Angular实现文件夹Zip下载:解决无法解压问题

Google Drive文件夹下载生成的Zip无法解压问题解决

我需要通过Google Drive API下载指定文件夹内的所有文件,已知API不支持批量下载,因此采用遍历文件夹内文件逐个下载的方案。单个文件下载已实现,但将文件夹内容打包为Zip后,生成的文件无法解压。

当前代码

后端Spring Boot代码

Controller层

@GetMapping("/download-folder/{id}")
public ResponseEntity<StreamingResponseBody> download(@PathVariable String id) throws GeneralSecurityException, IOException {
    return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).body(googleDriveService.downloadFolder(id));
}

Service层

public StreamingResponseBody downloadFolder(String id) throws GeneralSecurityException, IOException {
    Drive drive = getService();
    List<File> files = getAllFilesInFolder(id);

    StreamingResponseBody stream = output -> {
        files.forEach(file -> {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                drive.files().get(file.getId()).executeMediaAndDownloadTo(baos);
                output.write(baos.toByteArray());
                output.flush();
            } catch (IOException e) {
                log.error("Error when download file in folder ", e);
            }
        });
    };

    return stream;
}

前端Angular代码

Service层

downloadFolder(id: string): Observable<Blob> {
    return this.http.get(`${this.resourceUrl}/download-folder/${id}`, { responseType: 'blob' });
}

Component层

downloadFolder(): void {
    this.documentService.downloadFolder(this.folderId!).subscribe(res => saveAs(res, this.folderName + '.zip'));
}

问题原因

后端只是将多个文件的字节流直接拼接输出,没有遵循Zip格式的规范进行打包。前端把这种拼接后的字节流当作Zip文件保存,自然无法被解压工具识别为合法的Zip包。

解决方案

后端需要使用ZipOutputStream将每个下载的文件作为独立条目写入Zip包,生成符合规范的Zip流输出。

修改后后端Service层代码

public StreamingResponseBody downloadFolder(String id) throws GeneralSecurityException, IOException {
    Drive drive = getService();
    List<File> files = getAllFilesInFolder(id);

    StreamingResponseBody stream = output -> {
        try (ZipOutputStream zipOut = new ZipOutputStream(output)) {
            for (File file : files) {
                // 创建Zip条目,使用文件名称作为条目名
                ZipEntry zipEntry = new ZipEntry(file.getName());
                zipOut.putNextEntry(zipEntry);

                // 直接将Google Drive下载的内容写入Zip流,无需中转ByteArrayOutputStream
                drive.files().get(file.getId()).executeMediaAndDownloadTo(zipOut);
                
                zipOut.closeEntry();
            }
            zipOut.finish();
        } catch (IOException e) {
            log.error("Error when packaging folder into zip ", e);
            throw e;
        }
    };

    return stream;
}

优化Controller层(可选)

明确指定响应的MIME类型为Zip,并可直接在后端设置下载文件名:

@GetMapping("/download-folder/{id}")
public ResponseEntity<StreamingResponseBody> download(@PathVariable String id) throws GeneralSecurityException, IOException {
    // 假设getFolderName方法可根据文件夹ID获取名称
    String folderName = getFolderName(id);
    return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType("application/zip"))
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + folderName + ".zip\"")
            .body(googleDriveService.downloadFolder(id));
}

前端优化(可选)

如果后端设置了Content-Disposition头,可以通过响应头获取文件名,避免前端硬编码:

修改Service层返回类型

downloadFolder(id: string): Observable<HttpResponse<Blob>> {
    return this.http.get(`${this.resourceUrl}/download-folder/${id}`, { 
        responseType: 'blob',
        observe: 'response' 
    });
}

修改Component层逻辑

downloadFolder(): void {
    this.documentService.downloadFolder(this.folderId!).subscribe({
        next: (response) => {
            let fileName = this.folderName + '.zip';
            const contentDisposition = response.headers.get('Content-Disposition');
            if (contentDisposition) {
                const match = contentDisposition.match(/filename="?([^"]+)"?/);
                if (match && match.length > 1) {
                    fileName = match[1];
                }
            }
            saveAs(response.body!, fileName);
        },
        error: (error) => {
            console.error('文件夹下载失败', error);
        }
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 21:55:57