MS Graph SDK for Java上传200M以上Office文件到Teams异常问题求助
MS Graph SDK for Java上传大体积Office文件到Teams异常修复方案
问题复现条件
- 文件大小超过200M时稳定复现,100M以下Office文件上传无异常
- 仅MS Office类文件存在该问题,800M以内的文本文件、压缩包等非Office文件均可正常上传
- 绝大多数异常在上传临近结束时抛出,此时文件实际已成功上传至Teams,可正常查看和打开
- 少部分场景异常在上传过程中抛出,此时文件未成功上传至Teams
原始实现代码
public static void main(String[] args) { List<String> scopes = Arrays.asList("https://graph.microsoft.com/.default"); String fileName = "270MBwith_video.docx"; String localFilePath = "C:\\temp\\testfiles\\"+fileName; // Get an input stream for the file File file = new File(localFilePath); InputStream fileStream = null; try { fileStream = new FileInputStream(file); long streamSize = file.length(); // Create a callback used by the upload provider IProgressCallback callback = new IProgressCallback() { @Override // Called after each slice of the file is uploaded public void progress(final long current, final long max) { System.out.println(String.format("Uploaded %d bytes of %d total bytes", current, max)); } }; DriveItemCreateUploadSessionParameterSet uploadParams = DriveItemCreateUploadSessionParameterSet .newBuilder().withItem(new DriveItemUploadableProperties()).build(); TokenCredential clientSecretCredential = new ClientSecretCredentialBuilder() .clientId("61117459-d25c-4a98-a8bb-**********").clientSecret("0lyT.5zoqn*********") .tenantId("******.onmicrosoft.com").build(); TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(scopes, clientSecretCredential); GraphServiceClient<Request> graphClient = GraphServiceClient.builder() .authenticationProvider(tokenCredentialAuthProvider).buildClient(); // Create an upload session UploadSession uploadSession = graphClient .drives("b!Mca1ElNvN0C6oM-T4Tln0L0SJAZimN9Dkdbe****") .items("017N4JGSDWBUSNV4CPWZDJMLJZJFQZZS6X").itemWithPath(fileName).createUploadSession(null) .buildRequest().post(); LargeFileUploadTask<DriveItem> largeFileUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, graphClient, fileStream, streamSize, DriveItem.class); // Do the upload LargeFileUploadResult<DriveItem> item = largeFileUploadTask.upload(10 * 320 * 1024, null, callback); System.out.println("Uploaded to: " + item.responseBody.webUrl); } catch (Exception ex) { ex.printStackTrace(); } }
异常栈信息
Uploaded 271974400 bytes of 278847403 total bytes Uploaded 275251200 bytes of 278847403 total bytes com.microsoft.graph.core.ClientException: Request failed with error, retry if necessary. at com.microsoft.graph.tasks.LargeFileUploadRequest.upload(LargeFileUploadRequest.java:100) at com.microsoft.graph.tasks.LargeFileUploadTask.uploadAsync(LargeFileUploadTask.java:165) at com.microsoft.graph.tasks.LargeFileUploadTask.upload(LargeFileUploadTask.java:253) at testGraphSDK.largeFileUpload.main(largeFileUpload.java:72) Caused by: com.microsoft.graph.core.ClientException: Error executing the request at com.microsoft.graph.http.CoreHttpProvider.sendRequestInternal(CoreHttpProvider.java:406) at com.microsoft.graph.http.CoreHttpProvider.send(CoreHttpProvider.java:222) at com.microsoft.graph.tasks.LargeFileUploadRequest.upload(LargeFileUploadRequest.java:98) ... 3 more Caused by: java.net.SocketTimeoutException: Read timed out at java.base/java.net.SocketInputStream.socketRead0(Native Method) at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:168) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140) at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448) at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:68) at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1103) at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:823) at okio.Okio$2.read(Okio.java:140) at okio.AsyncTimeout$2.read(AsyncTimeout.java:237) at okio.RealBufferedSource.indexOf(RealBufferedSource.java:358) at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:230) at okhttp3.internal.http1.Http1ExchangeCodec.readHeaderLine(Http1ExchangeCodec.java:242) at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.java:213) at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.java:115) at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:94) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:43) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at com.microsoft.graph.httpcore.RedirectHandler.intercept(RedirectHandler.java:137) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at com.microsoft.graph.httpcore.RetryHandler.intercept(RetryHandler.java:176) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at com.microsoft.graph.httpcore.AuthenticationHandler.intercept(AuthenticationHandler.java:57) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at com.microsoft.graph.httpcore.TelemetryHandler.intercept(TelemetryHandler.java:68) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229) at okhttp3.RealCall.execute(RealCall.java:81) at com.microsoft.graph.http.CoreHttpProvider.sendRequestInternal(CoreHttpProvider.java:403) ... 5 more
根因说明
Teams/SharePoint后端在接收完Office文件全量数据后,会自动触发格式校验、在线预览预处理逻辑,大体积Office文件的预处理耗时远高于普通文件,超过了MS Graph SDK默认的HTTP读取超时阈值,就会抛出读超时异常。非Office文件不会触发该预处理流程,所以即使体积更大也不会超时。
修复方案
1. 自定义HTTP客户端超时配置
原代码中使用默认的Graph客户端配置,读取超时时间较短,手动调整超时参数适配Office文件预处理耗时:
// 构建自定义超时的OkHttpClient OkHttpClient okHttpClient = HttpClients.createDefault(clientSecretCredential, scopes) .newBuilder() .connectTimeout(2, TimeUnit.MINUTES) .writeTimeout(5, TimeUnit.MINUTES) .readTimeout(10, TimeUnit.MINUTES) // 拉长读超时时间适配Office文件预处理 .build(); // 使用自定义HTTP客户端构建GraphServiceClient GraphServiceClient<Request> graphClient = GraphServiceClient.builder() .authenticationProvider(tokenCredentialAuthProvider) .httpClient(okHttpClient) .buildClient();
2. 配置分片重试策略
原代码上传时未配置重试规则,添加上传重试逻辑应对偶发网络波动,同时调整分片大小为官方推荐的10M减少分片数量:
LargeFileUploadResult<DriveItem> item = largeFileUploadTask.upload(10 * 1024 * 1024, // 调整分片大小为10M new DefaultRetryHandler(5, 2, RetryHandler.DEFAULT_DELAY), // 配置最大5次指数退避重试 callback);
3. 添加超时兜底校验逻辑
捕获超时异常后,主动查询目标路径下的文件状态,避免文件实际上传成功但被判定为失败的情况:
catch (Exception ex) { if (ex instanceof ClientException && ex.getCause() instanceof SocketTimeoutException) { // 超时后查询目标路径文件是否存在 DriveItem uploadedItem = graphClient.drives("你的驱动器ID").items("目标文件夹ID").itemWithPath(fileName).buildRequest().get(); if (uploadedItem != null && uploadedItem.size.equals(streamSize)) { System.out.println("文件已成功上传,地址:" + uploadedItem.webUrl); return; } } ex.printStackTrace(); }
内容的提问来源于stack exchange,提问作者Matthew Hong
相关产品推荐
相关产品推荐

