关于钉钉分布上传的一些坑和BUG
提示:本博客需结合钉钉开发文档 ‘’新 “”老“ ” API 文档 使用
前言
钉钉内部程序免密登录若依实现后,需要用后台管理系统上传 必要的文件到钉盘或者群里
一、为啥使用分布上传
第三方程序 上传文件到钉钉 云盘 不能超过8MB 这远远不够
二、话不多说分布上传代码(所有不明白的东西需要结合官方新老文档查看)
代码如下(示例):
在这里插入代码片
package com.ruoyi.app.service;
import com.aliyun.dingtalkstorage_1_0.models.*;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
//import com.util.AccessTokenUtil;
//import lombok.val;
import org.testng.annotations.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import com.ruoyi.app.service.DingTalkService;
import javax.annotation.Resource;
public class MultipartUpload {
@Resource
public DingTalkService dingTalkService;
public static com.aliyun.dingtalkstorage_1_0.Client createClient() throws Exception {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
return new com.aliyun.dingtalkstorage_1_0.Client(config);
}
@Test
public void test() throws Exception {
com.aliyun.dingtalkstorage_1_0.Client client = MultipartUpload.createClient();
String spaceId = "钉盘文件夹ID";
String filePath = "D:\\filebak\\你的服务器上文件179期.pdf";
// 每片文件大小
long partSize = 1 * 1024 * 1024L; // 1MB
// 初始化分片上传接口
// String accessToken = dingTalkService.getAccessToken();
String accessToken ="你需要在钉钉开发者里创建";
String unionId = "你的unionID"; //可以通过手机号获取 userid 再用userid 获取unionID 你也可以用免登授权码获取userID
InitMultipartFileUploadHeaders initMultipartFileUploadHeaders = new InitMultipartFileUploadHeaders();
initMultipartFileUploadHeaders.xAcsDingtalkAccessToken = accessToken;
InitMultipartFileUploadRequest initMultipartFileUploadRequest = new InitMultipartFileUploadRequest();
initMultipartFileUploadRequest.setUnionId(unionId);
InitMultipartFileUploadRequest.InitMultipartFileUploadRequestOption initMultipartFileUploadRequestOption = new InitMultipartFileUploadRequest.InitMultipartFileUploadRequestOption();
initMultipartFileUploadRequestOption.setStorageDriver("DINGTALK");
initMultipartFileUploadRequest.setOption(initMultipartFileUploadRequestOption);
String uploadKey = client.initMultipartFileUploadWithOptions(spaceId, initMultipartFileUploadRequest, initMultipartFileUploadHeaders, new RuntimeOptions()).getBody().getUploadKey();
File file = new File(filePath);
long fileLength = file.length();
int partCount = (int) (fileLength / partSize) + ((fileLength % partSize != 0) ? 1 : 0);
for (int i = 0; i < partCount; i++) {
long startPos = i * partSize;
long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
// 获取分片上传信息
GetMultipartFileUploadInfosHeaders getMultipartFileUploadInfosHeaders = new GetMultipartFileUploadInfosHeaders();
getMultipartFileUploadInfosHeaders.xAcsDingtalkAccessToken = accessToken;
GetMultipartFileUploadInfosRequest getMultipartFileUploadInfosRequest = new GetMultipartFileUploadInfosRequest();
getMultipartFileUploadInfosRequest.setUnionId(unionId);
// getMultipartFileUploadInfosRequest.setUploadKey("第一步初始化分片上传接口获取的uploadKey");
getMultipartFileUploadInfosRequest.setUploadKey(uploadKey);
getMultipartFileUploadInfosRequest.setPartNumbers(Arrays.asList(i + 1));
GetMultipartFileUploadInfosResponseBody.GetMultipartFileUploadInfosResponseBodyMultipartHeaderSignatureInfosHeaderSignatureInfo headerSignatureInfo = client.getMultipartFileUploadInfosWithOptions(getMultipartFileUploadInfosRequest, getMultipartFileUploadInfosHeaders, new RuntimeOptions()).body.multipartHeaderSignatureInfos.get(0).headerSignatureInfo;
uploadPartFile(startPos, curPartSize, headerSignatureInfo, file);
}
// 提交文件,完成上传
CommitFileHeaders commitFileHeaders = new CommitFileHeaders();
commitFileHeaders.xAcsDingtalkAccessToken = accessToken;
CommitFileRequest.CommitFileRequestOption commitFileRequestOption
= new CommitFileRequest.CommitFileRequestOption()
.setSize(1024L)
.setConflictStrategy("OVERWRITE");
CommitFileRequest commitFileRequest = new CommitFileRequest()
.setUnionId(unionId)
// .setUploadKey(“第一步初始化分片上传接口获取的uploadKey”)
.setUploadKey(uploadKey)
.setName("自己取名字.pdf")
.setParentId("0")
.setOption(commitFileRequestOption);
//CommitFileResponseBody.CommitFileResponseBodyDentry dentry = client.commitFileWithOptions("存储空间ID", commitFileRequest, commitFileHeaders, new RuntimeOptions()).body.dentry;
CommitFileResponseBody.CommitFileResponseBodyDentry dentry = client.commitFileWithOptions(spaceId, commitFileRequest, commitFileHeaders, new RuntimeOptions()).body.dentry;
System.out.println(dentry);
}
private void uploadPartFile(long start, long size, GetMultipartFileUploadInfosResponseBody.GetMultipartFileUploadInfosResponseBodyMultipartHeaderSignatureInfosHeaderSignatureInfo headerSignatureInfo, File file) throws Exception {
InputStream inputStream = new FileInputStream(file);
if (start > 0) {
inputStream.skip(start);
}
URL url = new URL(headerSignatureInfo.getResourceUrls().get(0));
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
connection.setUseCaches(false);
if (headerSignatureInfo.getHeaders() != null) {
for (Map.Entry<String, String> entry : headerSignatureInfo.getHeaders().entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
connection.setReadTimeout(1000);
connection.setConnectTimeout(1000);
connection.connect();
OutputStream out = connection.getOutputStream();
byte[] buffer = new byte[1024];
long remain = size;
for (;remain > 0;) {
int sizeToRead = remain < buffer.length ? (int)remain : buffer.length;
int rsz = inputStream.read(buffer, 0, sizeToRead);
if (rsz < 0) {
break;
}
out.write(buffer, 0, rsz);
remain -= rsz;
}
out.flush();
out.close();
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
System.out.println("上传成功");
} else {
System.out.println("上传失败");
}
connection.disconnect();
}
}
钉钉 新老文档的坑注意 有两个文档 和两个 开发 Jar 包
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dingtalk</artifactId>
<version>2.0.14</version>
</dependency>
这两个文档 记录了 权限啥的 但是 分布上传 这些详细的东西 还是得用新文档 这是钉钉开发文档自己去找新的鉴权用老的
总结
钉钉文档有新旧 找不明白 看新文档 同时引入新老jar包 功能全面又好用
这里把Util 也发你们 还有 获取用户一些信息
package com.ruoyi.app.utils;
import cn.hutool.core.util.ObjectUtil;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.*;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.ruoyi.app.constant.DingTalkUrlConstant;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
/**
* 获取access_token工具类
*/
@Slf4j
public class DingTalkUtil {
public static Client creatClient() throws Exception {
Config config=new Config();
config.protocol="https";
config.regionId="central";
return new Client(config);
}
/**
* 在使用accessToken时,请注意:
* accessToken的有效期为7200秒(2小时),有效期内重复获取会返回相同结果并自动续期,过期后获取会返回新的accessToken。
* 开发者需要缓存accessToken,用于后续接口的调用。因为每个应用的accessToken是彼此独立的,所以进行缓存时需要区分应用来进行存储。
* 不能频繁调用接口,否则会受到频率拦截。
*
* @param corpId
* @param ssoSecret
* @return
*/
public static String getAccessToken(String corpId, String ssoSecret) throws Exception {
Client client = DingTalkUtil.creatClient();
GetSsoAccessTokenRequest getSsoAccessTokenRequest = new GetSsoAccessTokenRequest().setCorpid(corpId).setSsoSecret(ssoSecret);
try {
GetSsoAccessTokenResponse response = client.getSsoAccessToken(getSsoAccessTokenRequest);
if(ObjectUtil.isNotEmpty(response)){
return response.getBody().getAccessToken();
}
} catch (TeaException err) {
if (!Common.empty(err.code) && !Common.empty(err.message)) {
// err 中含有 code 和 message 属性,可帮助开发定位问题
// System.out.println("获取accessToken发生TeaException错误:"+err.message);
throw new Exception(err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!Common.empty(err.code) && !Common.empty(err.message)) {
// err 中含有 code 和 message 属性,可帮助开发定位问题
// System.out.println("获取accessToken发生Exception错误:"+err.message);
throw new Exception(err.getMessage());
}
}
return null;
}
public static String getAccessToken2(String appKey,String appSecret){
try {
DingTalkClient client = new DefaultDingTalkClient(DingTalkUrlConstant.GET_ACCESS_TOKEN_URL);
OapiGettokenRequest req = new OapiGettokenRequest();
req.setAppkey(appKey);
req.setAppsecret(appSecret);
req.setHttpMethod("GET");
OapiGettokenResponse rsp = client.execute(req);
//System.out.println(rsp.getBody());
return rsp.getAccessToken();
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取jsapi_ticket
* @param access_token
* @return
*/
public static String getJsapiTicket(String access_token) throws Exception {
Client client = DingTalkUtil.creatClient();
CreateJsapiTicketHeaders createJsapiTicketHeaders = new CreateJsapiTicketHeaders();
createJsapiTicketHeaders.xAcsDingtalkAccessToken=access_token;
try {
CreateJsapiTicketResponse response = client.createJsapiTicketWithOptions(createJsapiTicketHeaders, new RuntimeOptions());
if(ObjectUtil.isNotEmpty(response)){
return response.getBody().getJsapiTicket();
}
} catch (TeaException err) {
if (!Common.empty(err.code) && !Common.empty(err.message)) {
// err 中含有 code 和 message 属性,可帮助开发定位问题
// System.out.println("获取jsapiTicket发生TeaException错误:"+err.message);
throw new Exception(err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!Common.empty(err.code) && !Common.empty(err.message)) {
// err 中含有 code 和 message 属性,可帮助开发定位问题
// System.out.println("获取jsapiTicket发生Exception错误:"+err.message);
throw new Exception(err.getMessage());
}
}
return null;
}
}
还有一些自己写的service这个最好去看旧文档 直接copy
package com.ruoyi.app.service;
import cn.hutool.core.util.ObjectUtil;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetSsoUserInfoHeaders;
import com.aliyun.dingtalkoauth2_1_0.models.GetSsoUserInfoRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetSsoUserInfoResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.ruoyi.app.config.DingConfig;
import com.ruoyi.app.constant.DingTalkUrlConstant;
import com.ruoyi.app.model.SignatureParams;
import com.ruoyi.app.utils.DingTalkUtil;
import com.taobao.api.ApiException;
import com.taobao.api.FileItem;
import com.taobao.api.internal.util.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.net.URL;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.util.*;
@Slf4j
@Service
public class DingTalkService {
@Resource
private DingConfig dingConfig;
// 获取UserID
public String getUserId(String code) throws Exception {
try {
DingTalkClient client = new DefaultDingTalkClient(DingTalkUrlConstant.GET_USER_INFO_URL);
OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
req.setCode(code);
OapiV2UserGetuserinfoResponse rsp = client.execute(req, getAccessToken());
if(rsp.isSuccess()){
OapiV2UserGetuserinfoResponse.UserGetByCodeResponse userGetByCodeResponse = rsp.getResult();
return userGetByCodeResponse.getUserid();
} else {
throw new Exception(rsp.getErrmsg());
}
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
// 开启分块上传事务
public String upload(String code ,String accessToken) throws Exception {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/file/upload/transaction");
OapiFileUploadTransactionRequest request = new OapiFileUploadTransactionRequest();
request.setAgentId(dingConfig.getAgentId().toString());
request.setFileSize(1000L);
request.setChunkNumbers(1L);
request.setHttpMethod("GET");
OapiFileUploadTransactionResponse response = client.execute(request,accessToken);
System.out.println(response.getBody());
if(response.isSuccess()) {
return response.getUploadId();
} else {
throw new Exception(response.getErrmsg());
}
}
//上传文件块
public String uploadChunk(String accessToken) throws Exception {
OapiFileUploadChunkRequest request = new OapiFileUploadChunkRequest();
request.setAgentId(dingConfig.getAgentId().toString());
request.setChunkSequence(1L);
request.setUploadId("你的id");
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/file/upload/chunk?"+ WebUtils.buildQuery(request.getTextParams(),"utf-8"));
request = new OapiFileUploadChunkRequest();
//老文档 这些都废了
request.setFile(new FileItem("C://Users//ccy//Desktop//1.pdf"));
OapiFileUploadChunkResponse response = client.execute(request,accessToken);
System.out.println(response.getBody());
return response.getBody();
}
public Map getUserIdName(String code) throws Exception {
try {
DingTalkClient client = new DefaultDingTalkClient(DingTalkUrlConstant.GET_USER_INFO_URL);
OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
req.setCode(code);
OapiV2UserGetuserinfoResponse rsp = client.execute(req, getAccessToken());
if(rsp.isSuccess()){
OapiV2UserGetuserinfoResponse.UserGetByCodeResponse userGetByCodeResponse = rsp.getResult();
Map map = new HashMap();
map.put("userId",userGetByCodeResponse.getUserid());
map.put("userName",userGetByCodeResponse.getName());
return map;
} else {
throw new Exception(rsp.getErrmsg());
}
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
//提交文件上传事务
public String uploadTransaction(String accessToken) throws Exception {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/file/upload/transaction");
OapiFileUploadTransactionRequest request = new OapiFileUploadTransactionRequest();
request.setAgentId(dingConfig.getAgentId().toString());
request.setFileSize(1000L);
request.setChunkNumbers(1L);
request.setUploadId("你的上传ID");
request.setHttpMethod("GET");
OapiFileUploadTransactionResponse response = client.execute(request,accessToken);
System.out.println(response.getBody());
return response.getBody();
}
//上传文件转存到钉盘
public String dinpand (String code ,String accessToken) throws Exception{
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/cspace/add");
OapiCspaceAddRequest req = new OapiCspaceAddRequest();
req.setAgentId(dingConfig.getAgentId().toString());
req.setCode(code);
req.setMediaId("上传成功返回的ud");
req.setSpaceId("空间id");
req.setName("3.pdf");
req.setOverwrite(true);
req.setHttpMethod("GET");
OapiCspaceAddResponse rsp = client.execute(req, accessToken);
System.out.println(rsp.getBody());
return rsp.getBody();
}
// 获取钉钉用户详情
public String getUserInfo(String userId ,String access_token) throws Exception {
try {
DingTalkClient client = new DefaultDingTalkClient(DingTalkUrlConstant.USER_GET_URL);
OapiV2UserGetRequest req = new OapiV2UserGetRequest();
req.setUserid(userId);
req.setLanguage("en_US");
OapiV2UserGetResponse rsp = client.execute(req, access_token);
// System.out.println(rsp.getBody());
return rsp.getBody();
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
public String getlistparentbydept(String deptId ,String access_token) throws Exception {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listparentbydept");
OapiV2DepartmentListparentbydeptRequest req = new OapiV2DepartmentListparentbydeptRequest();
req.setDeptId(Long.valueOf(deptId));
OapiV2DepartmentListparentbydeptResponse rsp = client.execute(req, access_token);
// System.out.println(rsp.getBody());
return rsp.getBody();
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
public String getDeptName(Long deptId ,String access_token) throws Exception {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/get");
OapiV2DepartmentGetRequest req = new OapiV2DepartmentGetRequest();
req.setDeptId(deptId);
req.setLanguage("zh_CN");
OapiV2DepartmentGetResponse rsp = client.execute(req, access_token);
// System.out.println(rsp.getBody());
return rsp.getBody();
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
// 获取用户信息详情
//public GetSsoUserInfoResponse getSsoUserInfo(String code) throws Exception {
// try {
// Client client = new Client(
// dingConfig.getAppKey(),
// dingConfig.getAppSecret()
// );
// GetSsoUserInfoRequest getSsoUserInfoRequest = new GetSsoUserInfoRequest();
// getSsoUserInfoRequest.code = code;
// GetSsoUserInfoHeaders getSsoUserInfoHeaders = new GetSsoUserInfoHeaders();
// GetSsoUserInfoResponse getSsoUserInfoResponse = client.getSsoUserInfo(getSsoUserInfoRequest, getSsoUserInfoHeaders, new RuntimeOptions());
// if (getSsoUserInfoResponse.success) {
// return getSsoUserInfoResponse;
// } else {
// throw new Exception(getSsoUserInfoResponse.body);
// }
// } catch (TeaException e) {
// e.printStackTrace();
// }
// return null;
// }
// 获取accessToken
public String getAccessToken() throws Exception {
return DingTalkUtil.getAccessToken2(dingConfig.getAppKey(),dingConfig.getAppSecret());
}
public SignatureParams signature(String url) throws Exception{
String accessToken=getAccessToken();
String jsticket=DingTalkUtil.getJsapiTicket(accessToken);
String nonceStr=getRandomStr(10);
Long timeStamp=new Date().getTime();
String plain = "jsapi_ticket=" + jsticket + "&noncestr=" + nonceStr + "×tamp=" + String.valueOf(timeStamp)
+ "&url=" + decodeUrl(url);
SignatureParams signatureParams=new SignatureParams();
//ServiceResult result=ServiceResult.getSuccessResult();
signatureParams.setAgentId(dingConfig.getAgentId());
signatureParams.setCorpId(dingConfig.getCorpId());
signatureParams.setAccessToken(accessToken);
signatureParams.setJsticket(jsticket);
signatureParams.setNonceStr(nonceStr);
signatureParams.setTimeStamp(timeStamp);
signatureParams.setUrl(url);
try {
MessageDigest sha1 = MessageDigest.getInstance("SHA-256");
sha1.reset();
sha1.update(plain.getBytes("UTF-8"));
signatureParams.setSignature(byteToHex(sha1.digest()));
//return byteToHex(sha1.digest());
return signatureParams;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
// 字节数组转化成十六进制字符串
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
/**
* 因为ios端上传递的url是encode过的,android是原始的url。开发者使用的也是原始url,
* 所以需要把参数进行一般urlDecode
*
* @param url
* @return
* @throws Exception
*/
private static String decodeUrl(String url) throws Exception {
URL urler = new URL(url);
StringBuilder urlBuffer = new StringBuilder();
urlBuffer.append(urler.getProtocol());
urlBuffer.append(":");
if (urler.getAuthority() != null && urler.getAuthority().length() > 0) {
urlBuffer.append("//");
urlBuffer.append(urler.getAuthority());
}
if (urler.getPath() != null) {
urlBuffer.append(urler.getPath());
}
if (urler.getQuery() != null) {
urlBuffer.append('?');
urlBuffer.append(URLDecoder.decode(urler.getQuery(), "utf-8"));
}
return urlBuffer.toString();
}
public static String getRandomStr(int count) {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < count; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}
静态常量
package com.ruoyi.app.constant;
/**
* 钉钉开放接口网关常量
*/
public class DingTalkUrlConstant {
/**
* 获取access_token url
*/
public static final String GET_ACCESS_TOKEN_URL = "https://oapi.dingtalk.com/gettoken";
/**
* 获取jsapi_ticket url
*/
public static final String GET_JSAPI_TICKET_URL = "https://oapi.dingtalk.com/oauth2/jsapiTickets";
/**
* 通过免登授权码获取用户信息 url
*/
public static final String GET_USER_INFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo";
/**
* 根据用户id获取用户详情 url
*/
public static final String USER_GET_URL = "https://oapi.dingtalk.com/topapi/v2/user/get";
/**
* 发送消息 url
*/
public static final String SEND_MESSAGE_URL = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2";
}