You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Laravel应用从Google Drive下载混合文件异常:图片正常但Docs文件为空

解决Laravel中Google Drive下载Google Docs文件为空的问题

这个问题我之前踩过坑!核心原因是Google Docs系列文件(Docs/Sheets/Slides/Drawing等)和普通二进制文件(图片、PDF、压缩包等)在Google Drive的存储机制完全不同:普通文件是实打实的二进制数据,而Google Docs类文件是Google的在线专属格式——你直接调用常规下载接口拿到的只是一个空的占位文件,根本不是实际内容,必须通过Google Drive的文件导出接口来转换成可下载的格式。

问题拆解

  • 图片能正常下载:因为图片是标准二进制文件,Google Drive直接返回文件原始内容。
  • Google Docs文件下载为空:这类文件在Drive中是Google内部格式,没有直接的二进制内容,必须指定导出格式(比如docx/xlsx/pptx/PDF)后才能获取实际内容。

具体解决方案

我们需要修改下载逻辑,先判断文件类型,针对Google Docs类文件调用导出接口,普通文件保持原有逻辑即可。

1. 扩展GoogleDriveAdapter(暴露必要的API实例)

首先确保你的GoogleDriveAdapter类能暴露底层的Google_Service_Drive实例和文件夹ID,方便后续调用导出接口:

class GoogleDriveAdapter extends AbstractAdapter
{
    // ... 你的现有代码

    // 新增:返回Google Drive服务实例
    public function getService(): \Google_Service_Drive
    {
        return $this->service;
    }

    // 新增:返回绑定的文件夹ID
    public function getFolderId(): string
    {
        return $this->folderId;
    }
}

2. 修改Controller中的下载逻辑

替换原来的单一下载代码,加入文件类型判断和导出逻辑:

use Google_Service_Drive;
use Illuminate\Http\Response;

public function downloadFile(string $fileName)
{
    $disk = Storage::disk('googleConsole');
    $adapter = $disk->getAdapter();
    $driveService = $adapter->getService();
    $targetFolderId = $adapter->getFolderId();

    // 1. 根据文件名和父文件夹查找文件元数据
    $files = $driveService->files->listFiles([
        'q' => "name='{$fileName}' and '{$targetFolderId}' in parents",
        'fields' => 'files(id, mimeType, name)'
    ])->getFiles();

    if (empty($files)) {
        abort(404, '文件不存在');
    }
    $targetFile = $files[0];

    // 2. 定义Google Docs类型文件的MIME标识
    $googleDocMimes = [
        'application/vnd.google-apps.document',    // Google Docs
        'application/vnd.google-apps.spreadsheet', // Google Sheets
        'application/vnd.google-apps.presentation',// Google Slides
        'application/vnd.google-apps.drawing',     // Google Drawing
    ];

    // 3. 判断是否为Google Docs类文件,走导出逻辑
    if (in_array($targetFile->getMimeType(), $googleDocMimes)) {
        // 定义导出格式映射(可根据需求调整,比如换成PDF)
        $exportFormatMap = [
            'application/vnd.google-apps.document' => [
                'mime' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
                'ext' => 'docx'
            ],
            'application/vnd.google-apps.spreadsheet' => [
                'mime' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                'ext' => 'xlsx'
            ],
            'application/vnd.google-apps.presentation' => [
                'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
                'ext' => 'pptx'
            ],
            'application/vnd.google-apps.drawing' => [
                'mime' => 'image/png',
                'ext' => 'png'
            ],
        ];

        $exportConfig = $exportFormatMap[$targetFile->getMimeType()];
        // 调用Google Drive导出接口获取文件内容
        $exportedContent = $driveService->files->export(
            $targetFile->getId(),
            $exportConfig['mime'],
            ['alt' => 'media']
        );

        // 生成带正确后缀的文件名
        $exportFileName = pathinfo($fileName, PATHINFO_FILENAME) . '.' . $exportConfig['ext'];

        // 返回下载响应
        return response($exportedContent->getBody())
            ->header('Content-Type', $exportConfig['mime'])
            ->header('Content-Disposition', "attachment; filename=\"{$exportFileName}\"");
    }

    // 4. 普通文件走原有的下载逻辑
    return $disk->download($fileName);
}

关键注意点

  • 权限与API启用:确保你的Google Cloud项目已经启用了Google Drive API,并且授权的refreshToken包含https://www.googleapis.com/auth/drive权限(至少是文件读取权限)。
  • 导出格式可选:你可以根据需求修改exportFormatMap,比如把Docs导出为PDF(对应MIME类型application/pdf),Sheets导出为CSV(text/csv)等,具体支持的格式可以参考Google Drive API官方文档。
  • 文件名处理:原Google Docs文件的文件名可能没有后缀,导出时一定要加上对应格式的后缀,避免用户下载后无法识别文件类型。

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

火山引擎 最新活动