使用Graph API写入Excel异常:正常运行一段时间后失效
问题分析与解决方案
核心问题定位
你的问题(网页版Excel无法打开、Graph API写入失败,桌面版正常,复制文件后恢复),大概率是Graph API工作簿会话未正确释放导致的文件锁定。即使代码中调用了closeSession,仍可能存在会话残留的情况。
代码中的潜在问题
会话关闭可靠性不足
- 当前
close_session1仅判断请求是否报错,但Graph API的closeSession可能返回成功状态但实际会话未释放(比如网络波动导致请求未到达服务器)。 - 未处理
create_session1返回false的场景,若会话创建失败,后续操作会以无会话模式执行,隐式创建的临时会话无法自动释放。
- 当前
错误处理缺失导致会话泄漏
get_clients_from_tb1中,若post_data_to_excel1或delete_rows_not_in_data1执行出错,流程会直接终止,close_session1无法被调用,会话永久残留。
变量与会话ID传递错误
get_clients_from_tb1的Authorization头使用了未定义的$Token变量,应该传入函数参数$accessToken;- 需确认
post_data_to_excel1和delete_rows_not_in_data1是否在请求头中正确携带Workbook-Session-Id,未携带会导致Graph API自动创建新会话,引发会话溢出。
修复方案
1. 优化会话关闭逻辑(增加重试与状态校验)
修改close_session1,确保会话可靠关闭:
function close_session1($token, $sessionId, $retryTimes = 2) { $closeSessionUrl = "https://graph.microsoft.com/v1.0/users/{userid}/drive/items/{id}/workbook/closeSession"; $success = false; for ($i = 0; $i <= $retryTimes; $i++) { $closeSessionResponse = wp_remote_request($closeSessionUrl, [ 'method' => 'POST', 'headers' => [ 'Authorization' => "Bearer $token", 'Workbook-Session-Id' => $sessionId, ], ]); if (!is_wp_error($closeSessionResponse)) { $statusCode = wp_remote_retrieve_response_code($closeSessionResponse); if ($statusCode >= 200 && $statusCode < 300) { $success = true; break; } } sleep(1); // 重试前等待1秒 } if (!$success) { error_log("Failed to close session after $retryTimes retries: Session ID $sessionId"); } return $success; }
2. 全局异常捕获,保证会话必关
修改get_clients_from_tb1,用try...finally确保无论中间是否出错,会话都会被关闭:
function get_clients_from_tb1($accessToken){ $sessionid = null; try { $args = array( 'headers' => array( 'Authorization'=> "Bearer $accessToken", // 修复变量错误 ), ); $apiUrl = 'https://system.easypractice.net/api/v1/clients?page_size=500'; $response = wp_remote_get($apiUrl, $args); if (is_wp_error($response)) { $error_message = $response->get_error_message(); error_log("Something went wrong1: $error_message"); return; } $status = wp_remote_retrieve_response_code($response); if ($status !== 200) { error_log("Received HTTP status code: $status"); return; } $data = json_decode(wp_remote_retrieve_body($response), true); $all_client_ids_from_tb = array(); if (isset($data['data']) && is_array($data['data'])) { $sessionid = create_session1($accessToken); if (!$sessionid) { error_log("Failed to create workbook session"); return; } foreach ($data['data'] as $client_data) { $all_client_ids_from_tb[] = $client_data['id']; post_data_to_excel1($accessToken,$client_data,$sessionid); } delete_rows_not_in_data1($accessToken,$all_client_ids_from_tb,$sessionid); } } catch (Exception $e) { error_log("Unexpected error in get_clients_from_tb1: " . $e->getMessage()); } finally { if ($sessionid) { close_session1($accessToken,$sessionid); } } }
3. 强制清理残留会话
如果文件已经被锁定,调用以下函数列出并关闭所有残留会话:
function list_and_close_all_sessions($token) { $listSessionsUrl = "https://graph.microsoft.com/v1.0/users/{userid}/drive/items/{id}/workbook/sessions"; $response = wp_remote_get($listSessionsUrl, [ 'headers' => [ 'Authorization' => "Bearer $token", ], ]); if (!is_wp_error($response)) { $sessions = json_decode(wp_remote_retrieve_body($response), true); if (isset($sessions['value'])) { foreach ($sessions['value'] as $session) { close_session1($token, $session['id']); } } } }
可在cron任务开头调用此函数,定期清理会话。
4. 权限与文件配置检查
- 确认Azure应用已授予
Files.ReadWrite.All或Sites.ReadWrite.All权限; - 检查SharePoint文件属性,确保未设置为仅允许桌面版编辑。
预防措施
- 每次cron任务仅创建一个会话,操作完成后立即关闭;
- 增加会话创建、关闭的日志记录,便于排查问题;
- 定期监控文件会话数量,避免会话溢出导致锁定。
内容的提问来源于stack exchange,提问作者Oliver - bbh
相关产品推荐
相关产品推荐

