From 38161f96e158b2bfab33744b2f9b98f3cca39092 Mon Sep 17 00:00:00 2001 From: zhaojun <873019219@qq.com> Date: Sat, 26 Nov 2022 18:00:16 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E4=BC=98=E5=8C=96=E5=BE=AE?= =?UTF-8?q?=E8=BD=AF=20OneDrive=E3=80=81SharePoint=20=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E7=9A=84=E4=BB=A3=E7=A0=81,=20=E6=9B=B4?= =?UTF-8?q?=E5=A5=BD=E7=9A=84=E8=BE=93=E5=87=BA=E6=97=A5=E5=BF=97=E5=92=8C?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 +- .../base/AbstractMicrosoftDriveService.java | 251 ++++++++++-------- 2 files changed, 149 insertions(+), 107 deletions(-) diff --git a/pom.xml b/pom.xml index 3fb1d93..3aaeee7 100644 --- a/pom.xml +++ b/pom.xml @@ -187,7 +187,10 @@ bcprov-jdk15on 1.70 - + + org.springframework.retry + spring-retry + diff --git a/src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractMicrosoftDriveService.java b/src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractMicrosoftDriveService.java index 247014d..27775b6 100644 --- a/src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractMicrosoftDriveService.java +++ b/src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractMicrosoftDriveService.java @@ -2,22 +2,23 @@ package im.zhaojun.zfile.module.storage.service.base; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; -import cn.hutool.http.HttpRequest; +import cn.hutool.http.ContentType; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import im.zhaojun.zfile.module.storage.constant.StorageConfigConstant; -import im.zhaojun.zfile.module.storage.model.dto.OAuth2TokenDTO; -import im.zhaojun.zfile.module.storage.model.entity.StorageSourceConfig; -import im.zhaojun.zfile.module.storage.model.param.MicrosoftDriveParam; -import im.zhaojun.zfile.module.storage.service.StorageSourceConfigService; -import im.zhaojun.zfile.module.storage.model.bo.RefreshTokenCacheBO; import im.zhaojun.zfile.core.constant.ZFileConstant; import im.zhaojun.zfile.core.exception.StorageSourceRefreshTokenException; +import im.zhaojun.zfile.core.exception.ZFileRetryException; import im.zhaojun.zfile.core.util.StringUtils; +import im.zhaojun.zfile.module.storage.constant.StorageConfigConstant; +import im.zhaojun.zfile.module.storage.model.bo.RefreshTokenCacheBO; +import im.zhaojun.zfile.module.storage.model.dto.OAuth2TokenDTO; +import im.zhaojun.zfile.module.storage.model.entity.StorageSourceConfig; import im.zhaojun.zfile.module.storage.model.enums.FileTypeEnum; +import im.zhaojun.zfile.module.storage.model.param.MicrosoftDriveParam; import im.zhaojun.zfile.module.storage.model.result.FileItemResult; +import im.zhaojun.zfile.module.storage.service.StorageSourceConfigService; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.http.HttpEntity; @@ -25,6 +26,8 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.retry.RetryCallback; +import org.springframework.retry.support.RetryTemplate; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @@ -80,7 +83,18 @@ public abstract class AbstractMicrosoftDriveService

fileList(String folderPath) { folderPath = StringUtils.trimStartSlashes(folderPath); - String fullPath = StringUtils.concat(param.getBasePath(), folderPath); + String fullPath = StringUtils.concatTrimEndSlashes(param.getBasePath(), folderPath); List result = new ArrayList<>(); - String nextLink = null; + String nextPageLink = null; do { String requestUrl; - if (nextLink != null) { - nextLink = nextLink.replace("+", "%2B"); - requestUrl = URLUtil.decode(nextLink); - }else if (ZFileConstant.PATH_SEPARATOR.equalsIgnoreCase(fullPath) || "".equalsIgnoreCase(fullPath)) { + // 如果有下一页链接,则优先取下一页 + // 如果没有则判断是根目录还是子目录 + if (nextPageLink != null) { + nextPageLink = nextPageLink.replace("+", "%2B"); + requestUrl = URLUtil.decode(nextPageLink); + } else if (ZFileConstant.PATH_SEPARATOR.equalsIgnoreCase(fullPath) || "".equalsIgnoreCase(fullPath)) { requestUrl = DRIVER_ROOT_URL; } else { requestUrl = DRIVER_ITEMS_URL; } - fullPath = StringUtils.trimEndSlashes(fullPath); - - JSONObject root; - - HttpHeaders headers = new HttpHeaders(); - headers.set("storageId", storageId.toString()); - HttpEntity entity = new HttpEntity<>(headers); - - try { - root = oneDriveRestTemplate.exchange(requestUrl, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody(); - } catch (HttpClientErrorException e) { - log.error("调用 OneDrive 时出现了网络异常, 响应信息: {}, 已尝试重新刷新 token 后再试.", e.getResponseBodyAsString(), e); - refreshAccessToken(); - root = oneDriveRestTemplate.exchange(requestUrl, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody(); - } + JSONObject root = executeRetryableRequest(context -> { + int retryCount = context.getRetryCount(); + if (retryCount > 0) { + HttpClientErrorException ex = (HttpClientErrorException) context.getLastThrowable(); + log.warn("{} 调用 OneDrive 列表时出现了网络异常, 响应信息: [{}], 将尝试重新刷新 token 后再试. 文件路径为: [{}]", + getStorageSimpleInfo(), ex.getResponseBodyAsString(), fullPath, ex); + refreshAccessToken(); + } + + HttpEntity entity = getAuthorizationHttpEntity(); + return oneDriveRestTemplate.exchange(requestUrl, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody(); + }); + if (root == null) { return Collections.emptyList(); } - nextLink = root.getString("@odata.nextLink"); - JSONArray fileList = root.getJSONArray("value"); - for (int i = 0; i < fileList.size(); i++) { JSONObject fileItem = fileList.getJSONObject(i); - FileItemResult fileItemResult =jsonToFileItem(fileItem, folderPath); + FileItemResult fileItemResult = jsonToFileItem(fileItem, folderPath); result.add(fileItemResult); } - } while (nextLink != null); - + + nextPageLink = root.getString("@odata.nextLink"); + } while (nextPageLink != null); + return result; } @@ -236,19 +237,19 @@ public abstract class AbstractMicrosoftDriveService

entity = new HttpEntity<>(headers); - - JSONObject fileItem; - - try { - fileItem = oneDriveRestTemplate.exchange(DRIVER_ITEM_URL, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody(); - } catch (HttpClientErrorException e) { - log.error("调用 OneDrive 时出现了网络异常, 响应信息: {}, 已尝试重新刷新 token 后再试.", e.getResponseBodyAsString(), e); - refreshAccessToken(); - fileItem = oneDriveRestTemplate.exchange(DRIVER_ITEM_URL, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody(); - } + JSONObject fileItem = executeRetryableRequest(context -> { + int retryCount = context.getRetryCount(); + if (retryCount > 0) { + refreshAccessToken(); + HttpClientErrorException ex = (HttpClientErrorException) context.getLastThrowable(); + log.warn("{} 调用 OneDrive 获取文件信息时出现了网络异常, 响应信息: [{}], 将尝试重新刷新 token 后再试. 获取文件路径为: {}", + getStorageSimpleInfo(), ex.getResponseBodyAsString(), fullPath, ex); + + } + + HttpEntity entity = getAuthorizationHttpEntity(); + return oneDriveRestTemplate.exchange(DRIVER_ITEM_URL, HttpMethod.GET, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath).getBody(); + }); if (fileItem == null) { return null; @@ -262,7 +263,7 @@ public abstract class AbstractMicrosoftDriveService

data = new HashMap<>(); data.put("name", name); data.put("folder", new HashMap<>()); data.put("@microsoft.graph.conflictBehavior", "replace"); - HttpEntity entity = new HttpEntity<>(data, headers); + HttpEntity> entity = getAuthorizationHttpEntity(data); ResponseEntity responseEntity = oneDriveRestTemplate.exchange(requestUrl, HttpMethod.POST, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath); return responseEntity.getStatusCode().is2xxSuccessful(); } @@ -295,10 +292,7 @@ public abstract class AbstractMicrosoftDriveService

entity = new HttpEntity<>(headers); - + HttpEntity entity = getAuthorizationHttpEntity(); ResponseEntity responseEntity = oneDriveRestTemplate.exchange(DRIVER_ITEM_OPERATOR_URL, HttpMethod.DELETE, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath); return responseEntity.getStatusCode().is2xxSuccessful(); } @@ -307,12 +301,9 @@ public abstract class AbstractMicrosoftDriveService

entity = new HttpEntity<>(jsonObject, headers); + JSONObject jsonObject = new JSONObject().fluentPut("name", newName); + HttpEntity entity = getAuthorizationHttpEntity(jsonObject); ResponseEntity responseEntity = oneDriveRestTemplate.exchange(DRIVER_ITEM_OPERATOR_URL, HttpMethod.PATCH, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath); return responseEntity.getStatusCode().is2xxSuccessful(); } @@ -326,10 +317,7 @@ public abstract class AbstractMicrosoftDriveService

entity = new HttpEntity<>(headers); - + HttpEntity entity = getAuthorizationHttpEntity(); ResponseEntity responseEntity = oneDriveRestTemplate.exchange(CREATE_UPLOAD_SESSION_URL, HttpMethod.POST, entity, JSONObject.class, getGraphEndPoint(), getType(), fullPath); @@ -388,7 +376,7 @@ public abstract class AbstractMicrosoftDriveService

+ * 该对象默认包含了当前存储源的 AccessToken. + * + * @return HttpEntity 对象 + */ + private HttpEntity getAuthorizationHttpEntity() { + return getAuthorizationHttpEntity(null); + } + + + /** + * 获取存储源默认的 HttpEntity 对象. + *
+ * 该对象默认包含了当前存储源的 AccessToken. + * + * @param body + * 请求体 + * + * @return HttpEntity 对象 + */ + private HttpEntity getAuthorizationHttpEntity(T body) { + HttpHeaders headers = new HttpHeaders(); + StorageSourceConfig accessTokenConfig = + storageSourceConfigService.findByStorageIdAndName(storageId, StorageConfigConstant.ACCESS_TOKEN_KEY); + headers.setBearerAuth(accessTokenConfig.getValue()); + return new HttpEntity<>(body, headers); + } + + + /** + * 执行可重试 1 次的任务, 对抛出的异常转为 ZFileRetryException(Unchecked Exception) + * @param retryCallback 可重试的任务 + * @return 任务执行结果 + * + * @param 任务执行结果类型 + */ + private T executeRetryableRequest(RetryCallback retryCallback) { + RetryTemplate retryTemplate = RetryTemplate.builder().maxAttempts(2).retryOn(HttpClientErrorException.class).build(); + + T result; + try { + result = retryTemplate.execute(retryCallback); + } catch (Throwable e) { + throw new ZFileRetryException("请求失败", e); + } + + return result; + } } \ No newline at end of file