对接前提条件:拥有 accessKey、secretKey
步骤一:application.yml配置公私钥、并设置host,也就是访问金山的ip
步骤二:自定义接口,生成在线编辑访问链接,供给前端访问调用
Controller:只需要传文件Id
/**
* 方法描述:【在线编辑 】
*
* @author hdj
* @date 2023-06-28 13:43
*/
@RequestMapping(value = "/onlineEditing", method = RequestMethod.GET)
@ResponseBody
public Object onlineEditing(@RequestParam("fileId") String fileId) throws UnsupportedEncodingException {
return service.onlineEditing(fileId);
}
ServiceImpl:
@Autowired
private Environment env;
/**
* 方法描述:【在线编辑 】
*
* @author hdj
* @date 2023年06月14日 14点42分46秒
*/
public Object onlineEditing(String fileId) throws UnsupportedEncodingException {
String host = env.getProperty("wps.office.host");
String accessKey = env.getProperty("wps.office.accessKey");
String secretKey = env.getProperty("wps.office.secretKey");
if (!StringUtils.hasLength(host) || !StringUtils.hasLength(accessKey) || !StringUtils.hasLength(secretKey)) {
throw new LocalException("缺失环境配置");
}
String userId = UserUtils.getLoginUserId();
String userName = NameCache.getUserName(userId);
StringBuilder builder = new StringBuilder();
String url = builder.append("/api/edit/v1/files/")
.append(fileId).append("/link")
.append("?")
.append("type=w&_w_third_type=write")
.append("&_w_third_user_id=").append(userId)
.append("&_w_third_user_name=").append(URLEncoder.encode(userName, "UTF-8"))
.toString();
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = dateFormat.format(new Date());
String contentType = "application/json";
String body = "";
WPS4Signature signature = new WPS4Signature(accessKey, secretKey);
Map<String, String> headers = signature.getSignatureHeaders2("GET", url, body, date, contentType);
String result = HttpUtil.createGet(host.concat("/open").concat(url)).addHeaders(headers).execute().body();
JSONObject resultObj = JSONObject.parseObject(result);
return resultObj;
}
HMacUtils:
/**
* @date 2022/5/20$ 18:20$
*/
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HMacUtils {
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString();
}
/**
* 利用java原生的摘要实现SHA256加密
* @param str 加密后的报文
* @return
*/
public static String getSHA256StrJava(byte[] str){
MessageDigest messageDigest;
String encodeStr = "";
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str);
encodeStr = byte2Hex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return encodeStr;
}
/**
* 将byte转为16进制
* @param bytes
* @return
*/
private static String byte2Hex(byte[] bytes){
StringBuffer stringBuffer = new StringBuffer();
String temp = null;
for (int i=0;i<bytes.length;i++){
temp = Integer.toHexString(bytes[i] & 0xFF);
if (temp.length()==1){
//1得到一位的进行补0操作
stringBuffer.append("0");
}
stringBuffer.append(temp);
}
return stringBuffer.toString();
}
}
WPS4Signature:
import org.apache.commons.lang3.StringUtils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* WPS-3签名,需要引入依赖包
* <pre>
* org.apache.commons.commons-lang3
* commons-codec.commons-codec
* </pre>
*/
public class WPS4Signature {
public final static String HTTP_HEADER_AUTHORIZATION = "Wps-Docs-Authorization";
public final static String HTTP_HEADER_DATE = "Wps-Docs-Date";
public final static String HTTP_HEADER_CONTENT_TYPE = "Content-Type";
private String appId; // 应用id
private String secretKey; // 应用秘钥
public WPS4Signature(String appId, String secretKey) {
this.appId = appId;
this.secretKey = secretKey;
}
/**
* 获取请求body MD5
*
* @param content 请求body
* @return
*/
public String getSHA256(String content) {
if (StringUtils.isBlank(content)) {
return HMacUtils.getSHA256StrJava("".getBytes());
} else {
return HMacUtils.getSHA256StrJava(content.getBytes());
}
}
/**
* 获取日期字符串
*
* @param date
* @return
*/
public static String getGMTDateString(Date date) {
SimpleDateFormat format = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
return format.format(date) + " GMT";
}
/**
* 签名
*
* @param uriWithQuerystring
* @param contentMD5 签名参数MD5
* @param dateString
* @return
*/
public String getSignature(String method, String uriWithQuerystring, String contentMD5, String dateString, String contentType) throws Exception {
return HMacUtils.HMACSHA256("WPS-4" + method + uriWithQuerystring + contentType + dateString + contentMD5, this.secretKey);
}
/**
* 获取X-Auth
*
* @param uriWithQuerystring 请求url,带querystring
* @param contentMD5 请求body MD5
* @param dateString 日期字符串,例如:Mon, 15 Nov 2021 02:34:04 GMT
* @param contentType application/json
* @return
*/
public String getAuthorization(String method, String uriWithQuerystring, String contentMD5, String dateString, String contentType) throws Exception {
String authorization = String.format(Locale.US, "WPS-4 %s:%s",
this.appId,
getSignature(method, uriWithQuerystring, contentMD5, dateString, contentType));
return authorization;
}
/**
* 获取签名请求头
*
* @param uriWithQuerystring 请求url,带querystring
* @param content 请求body
* @param date 日期,默认为 new Date()
* @param contentType 默认为 application/json
* @return
*/
public Map<String, String> getSignatureHeaders(String method, String uriWithQuerystring, String content, Date date, String contentType) {
if (uriWithQuerystring == null) {
uriWithQuerystring = "";
}
if (content == null || StringUtils.isBlank(content)) {
content = "";
} else {
content = getSHA256(content);
}
if (date == null) {
date = new Date();
}
if (contentType == null) {
contentType = "application/json";
}
String dateString = getGMTDateString(date);
String authorization = "";
try {
authorization = getAuthorization(method, uriWithQuerystring, content, dateString, contentType);
} catch (Exception e) {
}
Map<String, String> headers = new HashMap<>();
headers.put(WPS4Signature.HTTP_HEADER_AUTHORIZATION, authorization);
headers.put(WPS4Signature.HTTP_HEADER_CONTENT_TYPE, contentType);
headers.put(WPS4Signature.HTTP_HEADER_DATE, dateString);
return headers;
}
/**
* 获取签名请求头
*
* @param uriWithQuerystring 请求url,带querystring
* @param content 请求body
* @param date 日期,默认为 new Date()
* @param contentType 默认为 application/json
* @return
*/
public Map<String, String> getSignatureHeaders2(String method, String uriWithQuerystring, String content, String date, String contentType) {
if (uriWithQuerystring == null) {
uriWithQuerystring = "";
}
if (content == null || StringUtils.isBlank(content)) {
content = "";
} else {
content = getSHA256(content);
}
if (contentType == null) {
contentType = "application/json";
}
String dateString = date;
String authorization = "";
try {
authorization = getAuthorization(method, uriWithQuerystring, content, dateString, contentType);
} catch (Exception e) {
}
Map<String, String> headers = new HashMap<>();
headers.put(WPS4Signature.HTTP_HEADER_AUTHORIZATION, authorization);
headers.put(WPS4Signature.HTTP_HEADER_CONTENT_TYPE, contentType);
headers.put(WPS4Signature.HTTP_HEADER_DATE, dateString);
return headers;
}
}
步骤三:实现两个回调接口,分别是“文件信息获取接口”、“文件保存接口”
所需对象
FileModel :
@Data
public class FileModel implements Serializable {
private static final long serialVersionUID = 1L;
/** 文件id 不超过64位**/
public String id;
/** 文件名称,带后缀 **/
public String name;
/** 版本号,位数小于11位 **/
public Integer version;
/** 文件大小,字节,文件太大会出异常 **/
public Integer size;
/** 创建者id 不超过32位 **/
public String creator;
/** 创建时间,时间戳,单位:秒 **/
public Integer create_time;
/** 修改者id **/
public String modifier;
/** 修改时间,时间戳,单位:秒 **/
public Integer modify_time;
/** 文档下载地址 **/
public String download_url;
/** 限制预览页数,具体参考金山文档 **/
public Integer preview_pages;
/** 用户权限模块 **/
public UserAclModel user_acl;
/** 水印模块 **/
public WaterMarkModel watermark;
}
UserAclModel:
@Data
public class UserAclModel implements Serializable {
private static final long serialVersionUID = 1L;
/** 重命名权限,1 打开,0 关闭,默认 0 **/
public Integer rename;
/** 历史文件打开权限,1 打开,0 关闭,默认 1 **/
public Integer history;
/** 复制权限,1 打开,0 关闭,默认 1 **/
public Integer copy;
/** 导出权限,1 打开,0 关闭,默认 1 **/
public Integer export;
/** 打印权限,1 打开,0 关闭,默认 1 **/
public Integer print;
/** 只读情况下可评论权限,1 打开,0 关闭,默认 0 **/
public Integer comment;
/** 限制在当前系统内复制粘贴,1 打开,0 关闭,默认 0 **/
public Integer copy_control;
}
WaterMarkModel:
@Data
public class WaterMarkModel implements Serializable {
private static final long serialVersionUID = 1L;
/** 水印类型: 0 无水印,1 文字水印 **/
public Integer type;
/** 水印的文字 **/
public String value;
/** 水印颜色 **/
public String filestyle;
/** 字体 **/
public String font;
/** 水印旋转度 **/
public Float rotate;
/** 水印水平间距 默认 50 **/
public Integer horizontal;
/** 水印垂直间距 默认 100 **/
public Integer vertical;
}
UserModel:
@Data
public class UserModel implements Serializable {
private static final long serialVersionUID = 1L;
/** 用户id 不超过64位**/
public String id;
/** 用户名称 **/
public String name;
/** 用户操作权限: write 编辑, read 只读 **/
public String permission;
/** 用户头像地址, 支持url和base64 **/
public String avatar_url;
}
文件信息获取接口:
Controller:_w_third前缀为自定义参数
/**
* 方法描述:【附件信息获取 】
*
* @author hdj
* @date 2023-06-28 13:43
*/
@RequestMapping(value = "/v1/3rd/file/info", method = RequestMethod.GET)
@ResponseBody
public Object getFileInfo(@RequestParam("_w_third_type") String type, @RequestParam("_w_third_user_id") String userId, @RequestParam("_w_third_user_name") String userName, HttpServletRequest request) {
return service.getFileInfo(type, userId, userName, request);
}
ServiceImpl:获取数据库的dao对象自己修改成所需要的。其次需要提供一个自定义在线下载文件链接。
/**
* 方法描述:【附件信息获取 】
*
* @author hdj
* @date 2023年06月14日 14点42分46秒
*/
public Object getFileInfo(String type, String userId, String userName, HttpServletRequest request) {
String host = env.getProperty("wps.office.host");
JSONObject resultObject = new JSONObject();
FileModel fileModel = new FileModel();
UserModel userModel = new UserModel();
resultObject.put("file", fileModel);
resultObject.put("user", userModel);
String fileId = request.getHeader("x-weboffice-file-id");
FlowAttachment attachment = fileInfoDao.get(fileId);
if (Objects.isNull(attachment)) {
throw new ParamException("文件不存在");
}
try {
//设置文件信息
fileModel.setId(attachment.getId());
fileModel.setName(attachment.getFileName());
fileModel.setVersion(Objects.isNull(attachment.getVersion()) ? 1 : attachment.getVersion());
fileModel.setSize(Integer.valueOf(attachment.getFileSize()));
fileModel.setCreator(attachment.getCreateBy());
fileModel.setCreate_time((int) (attachment.getCreateDate().getTime() / 1000));
fileModel.setModifier(attachment.getUpdateBy());
fileModel.setModify_time((int) (attachment.getUpdateDate().getTime() / 1000));
fileModel.setDownload_url(host + attachment.getFilePath());
fileModel.setPreview_pages(StaticValue.STATIC_ZERO_INT);
//设置用户信息
userModel.setId(userId);
userModel.setName(userName);
userModel.setPermission(type);
} catch (Exception e) {
e.printStackTrace();
}
log.info("在线编辑文件信息: {}", resultObject);
return resultObject;
}
文件保存接口:
Controller:
/**
* 方法描述:【附件信息存储 】
*
* @author hdj
* @date 2023-06-28 13:43
*/
@RequestMapping(value = "/v1/3rd/file/save", method = RequestMethod.POST)
@ResponseBody
public Object save(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
return service.save(file, request);
}
ServiceImpl:获取数据库Dao逻辑需自己修改,数据库表需要有一个version版本号字段
/**
* 方法描述:【附件信息存储 】
*
* @author hdj
* @date 2023年06月14日 14点42分46秒
*/
public Object save(MultipartFile file, HttpServletRequest request) {
String fileId = request.getHeader("x-weboffice-file-id");
FlowAttachment attachment = fileInfoDao.get(fileId);
if (Objects.isNull(attachment)) {
throw new ParamException("文件不存在");
}
if (!file.isEmpty()) {
String filePath = AttachmentConfig.fileUploadFolder + attachment.getFilePath();
try {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(filePath)));
out.write(file.getBytes());
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//更新版本号
Integer version = Objects.isNull(attachment.getVersion()) ? 2 : attachment.getVersion() + 1;
attachment.setVersion(version);
fileInfoDao.updateSelective(attachment);
String host = env.getProperty("wps.office.host");
JSONObject resultModel = new JSONObject();
JSONObject fileModel = new JSONObject();
fileModel.put("id", attachment.getId());
fileModel.put("name", attachment.getFileName());
fileModel.put("version", attachment.getVersion());
fileModel.put("size", Integer.valueOf(attachment.getFileSize()));
fileModel.put("download_url", host + attachment.getFilePath());
resultModel.put("file", fileModel);
log.info("在线保存文件信息: {}", resultModel);
return resultModel;
}
步骤四:编写好回调接口后,设置回调地址
在哪里设置?我这个是金山给的应用管理平台上进行设置的