如何通过Google Drive/Docs API获取文档版本历史的具体变更内容
问题
已通过Google OAuth 2.0完成Google Docs/Drive API对接,采用PHP+Composer开发,目前能获取文档的Revision ID和修改时间,但无法获取各版本的具体变更信息。需要实现:
- 通过JS/HTML按钮触发,根据版本ID或时间展示对应版本的具体变更
- 文本新增用绿色标注、删除用红色标注
- 支持识别图片新增等变更
当前代码:
<?php require_once 'vendor/autoload.php'; session_start(); $client = new Google_Client(); $client->setAuthConfig('client_secrets.json'); // Path to your client_secrets.json file $client->addScope(Google_Service_Drive::DRIVE_METADATA_READONLY); $client->addScope(Google_Service_Docs::DOCUMENTS_READONLY); $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'); // Path to your OAuth 2.0 callback file if (isset($_SESSION['access_token']) && $_SESSION['access_token']) { $client->setAccessToken($_SESSION['access_token']); $driveService = new Google_Service_Drive($client); // Query to get Google Documents owned by the user $optParams = array( 'q' => "'me' in owners and mimeType='application/vnd.google-apps.document'", 'fields' => 'files(id, name)', ); $results = $driveService->files->listFiles($optParams); if (count($results->getFiles()) == 0) { print "No Google Documents found.\n"; } else { $userInfo = $driveService->about->get(['fields' => 'user']); $googleUsername = $userInfo->getUser()->getDisplayName(); echo "Hello : " . $googleUsername . "<br><br>"; print "Google Documents:<br>"; foreach ($results->getFiles() as $file) { printf("Document: %s (%s)<br>", $file->getName(), $file->getId()); $revisions = $driveService->revisions->listRevisions($file->getId()); foreach ($revisions->getRevisions() as $revision) { $modTime = $revision->getModifiedTime(); echo "Revision ID: " . $revision->getId() . ", Modified Time: " . $modTime . "<br>"; } echo "<hr>"; // Add a horizontal rule } } } else { $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'; // Path to your OAuth 2.0 callback file header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } ?>
解决方案
1. 权限调整
Google Drive API的DRIVE_METADATA_READONLY权限仅能获取元数据,无法读取版本内容,需要将其替换为DRIVE_READONLY,保留DOCUMENTS_READONLY用于解析文档结构:
$client->addScope(Google_Service_Drive::DRIVE_READONLY); $client->addScope(Google_Service_Docs::DOCUMENTS_READONLY);
2. 获取历史版本内容
通过Drive API的revisions->get方法,可导出指定版本的文档为JSON/HTML格式,JSON更适合结构化解析文本和图片:
// 在遍历revision的循环中添加 $revisionId = $revision->getId(); // 导出JSON格式的历史版本内容 $revisionContent = $driveService->revisions->get($file->getId(), $revisionId, [ 'alt' => 'media', 'mimeType' => 'application/vnd.google-apps.document+json' ])->getBody()->getContents();
3. 版本差异对比
文本差异
使用文本对比库(如sebastian/diff,通过Composer安装)生成差异标记,或前端用diff-match-patchJS库处理。示例后端处理:
// 安装依赖:composer require sebastian/diff use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; // 获取当前版本文本和历史版本文本 $currentText = extractDocText($docsService->documents->get($fileId)); $revisionText = extractRevisionText(json_decode($revisionContent, true)); // 生成差异 $builder = new UnifiedDiffOutputBuilder("--- 当前版本\n+++ 历史版本 $revisionId\n"); $differ = new Differ($builder); $diff = $differ->diff($currentText, $revisionText);
图片变更
解析JSON中的inlineObjects字段,对比当前版本与历史版本的图片ID,找出新增图片:
$currentImages = extractDocImages($docsService->documents->get($fileId)); $revisionImages = extractRevisionImages(json_decode($revisionContent, true)); $newImages = array_diff_key($revisionImages, $currentImages);
4. 前端交互与样式
给每个版本添加触发按钮,通过AJAX获取差异数据并渲染:
<!-- PHP遍历revision时输出按钮 --> <button onclick="showDiff('<?php echo $file->getId(); ?>', '<?php echo $revisionId; ?>')">查看变更</button> <div id="diff-<?php echo $file->getId().'-'.$revisionId; ?>" class="diff-container"></div> <script> function showDiff(fileId, revisionId) { fetch(`get_diff.php?fileId=${fileId}&revisionId=${revisionId}`) .then(res => res.json()) .then(data => { document.getElementById(`diff-${fileId}-${revisionId}`).innerHTML = data.html; }); } </script> <style> .diff-added { color: green; } .diff-deleted { color: red; text-decoration: line-through; } .diff-header { font-weight: bold; color: #666; } .diff-container { margin: 10px 0; padding: 10px; border: 1px solid #eee; } </style>
5. 辅助函数示例
// 提取当前版本文档文本 function extractDocText($doc) { $text = ''; foreach ($doc->getBody()->getContent() as $content) { if (property_exists($content, 'paragraph')) { foreach ($content->getParagraph()->getElements() as $elem) { if (property_exists($elem, 'textRun')) { $text .= $elem->getTextRun()->getContent(); } } } } return $text; } // 提取历史版本文档文本 function extractRevisionText($revisionDoc) { $text = ''; foreach ($revisionDoc['body']['content'] as $content) { if (isset($content['paragraph'])) { foreach ($content['paragraph']['elements'] as $elem) { if (isset($elem['textRun'])) { $text .= $elem['textRun']['content']; } } } } return $text; } // 提取当前版本图片 function extractDocImages($doc) { $images = []; foreach ($doc->getInlineObjects() as $id => $obj) { if (property_exists($obj, 'inlineObjectProperties') && property_exists($obj->getInlineObjectProperties(), 'imageProperties')) { $images[$id] = $obj->getInlineObjectProperties()->getImageProperties()->getContentUri(); } } return $images; }
内容的提问来源于stack exchange,提问作者Polarr
相关产品推荐
相关产品推荐

