WPS网页游览(新)
一、账号篇
1、 账号注册及应用注册
【地址】solution.wps.cn
步骤 1: 登录 WPS 账号
在使用 WebOffice 服务前,需要先登录您的 WPS 账号。(如果您已登录,请跳过该步骤)
步骤 2: 开发者认证
进入WebOffice 控制台,如果您之前没有认证过开发者,需要在这里进行开发者认证。
填写开发者信息,您需要根据字段提示如实填写您所在企业的信息,并上传一张符合要求的营业执照。
提交审核
步骤 3: 创建测试应用
在 WebOffice 控制台完成开发者认证后即可创建测试应用,测试应用可用于接入方体验 WebOffice、格式转换服务的功能。按照要求填写信息后,点击创建即可。创建完成后可在应用信息 tab 下查看应用的 AppID 和 AppSecret。
开放平台的 WebOffice、格式转换服务均提供两种应用类型,分别为测试应用和正式应用:
测试应用:面向研发和调试阶段,开发者可以在这里完成应用的整体功能研发、测试、联调以及产品验收,应用创建后即为测试阶段。
正式应用:面向正式上线阶段,方便开发者将测试应用验收通过的代码、服务、资源等部署到生产环境,应用上架需要提供应用的系统截图。
两者之间具体的异同点如下表所示:
服务类型 | 应用类型 | 文档并发数 | 文件大小限制 | 是否有水印 | 应用数量 |
---|---|---|---|---|---|
WebOffice | 测试应用 | ≤5 | ≤5MB | 是 | 2 个 |
正式应用 | 默认1024,可配置为无限制 | 默认100M,最大可配置为500M | 否 | 无限制 |
服务类型 | 应用类型 | 文档转换次数 | 文件大小限制 | 是否有水印 | 应用数量 |
---|---|---|---|---|---|
格式转换 | 测试应用 | 免费赠送500次 | ≤5MB | 是 | 2 个 |
正式应用 | 无限制 | 500M(超过请 联系商务开通) | 否 | 无限制 |
【大概基础知识到这里就可以了。详情请查看WPS】
二、配置篇
要实现WebOffice的在线预览功能需要前后端一同参与。其中涉及文件服务的回调配置部分。
1、回调网关 配置
回调网关目前支持 http 与 https 两种协议,支持以下两种范式:
范式一:scheme://host/path_prefix
(/path_prefix 可以为空)
示例:
http://solution.wps.cn/dev/WebOffice
https://solution.wps.cn
范式二:scheme://ip:port/prefix
(IP 必须为公网 IP 段)
示例:
http://117.78.0.2:8080/dev/WebOffice
http://117.78.0.2:8080
solution.wps.cn
和ip
为我系统服务的地址。这个地址用于WPS访问我们所开发的接口进行数据校验。本质就是 前端 -> WPS -> 后端。此处就是我们后端服务的地址。
【调用格式】 WebOffice 将通过回调网关拼接上 API 接口地址,然后以回调的方式请求对接方的这些接口。最终 WebOffice 集群将会向 回调网关+回调接口 发起请求。
以下为例子:
回调网关:https://solution.wps.cn/dev/WebOffice
接口:GET /v3/3rd/files/:file_id
文件 ID:1234567
最终请求地址:https://solution.wps.cn/dev/WebOffice/v3/3rd/files/1234567
2、配置流程
第一步:进入回调配置 #
进入控制台后,点击【应用管理】-【应用名称】-【回调配置】选项卡
第二步: 配置回调网关
假设网关地址为https://www.****.com:####
,回调接口地址是 /v3/3rd/files/${file_id}
,则完整的回调接口地址就是 https://www.****.com:####/v3/3rd/files/${file_id}
。
第三步:接口调试
针对每一个回调接口,可通过点击【调试】按钮,测试此接口能否被 WebOffice 服务器正常访问,以及接口返回的数据是否符合要求,您可以根据接口的调试信息逐步完善接口的逻辑。
token、file_id 从哪获取?
token
由接入方自定义, WebOffice 将会在回调接口时通过 X-Weboffice-Token
的 Header
字段回传,可用于检查鉴权。
file_id
可由数字、字母和下划线组成,但不能以下划线开头,它是由对接企业自己生成并管理,需要保证一个 file_id
对应一个文件,也对应一个文件的多个版本。
三、开发篇(后端)
需实现获取文件信息(GET /v3/3rd/files/:file_id)
,获取文件下载地址 (GET /v3/3rd/files/:file_id/download)
,文档用户权限(GET /v3/3rd/files/:file_id/permission)
具体请参考,开发文档。
其中还有个细节问题,返回的格式需按照文档要求进行,例如:code = 0 为成功。
具体部分代码,包含业务系统鉴权部分;
1、用户token鉴权
private UserToken getBaseUser(HttpServletRequest request) {
String token = request.getHeader("x-wps-weboffice-token");
if (token == null) {
token = request.getHeader("X-WebOffice-Token");
}
if (token == null) {
token = request.getHeader(BaseConstants.ACCESS\_TOKEN);
}
if (StringUtils.isEmptyWithTrim(token)) {
return null;
}
String username = JwtUtils.getUserId(token);
if (StringUtils.isEmptyWithTrim(username)) {
return null;
}
String type = "";
if(username.contains(BaseUser.USERNAME\_SPLIT)) {
int index = username.lastIndexOf(BaseUser.USERNAME\_SPLIT);
type = username.substring(index + BaseUser.USERNAME\_SPLIT.length());
username = username.substring(0, index);
}
String redisKey = BaseRedisConstants.getRedisKey(BaseRedisConstants.TOKEN\_USER.concat(type), username);
String value = RedisUtilEx.get(redisKey);
if(StringUtils.isEmptyWithTrim(value)) {
return null;
}
UserToken userToken = JsonUtils.json2Bean(value, UserToken.class);;
if(userToken == null) {
return null;
}
if(!token.equals(userToken.getAccessToken())) {
return null;
}
return userToken;
}
2、回调接口
WPS文件信息校验接口
@GetMapping("/v3/3rd/files/{fileId}")
@Log(title = "WPS文件信息校验接口")
public ResponseEntity<Object> getFilesInfo(@PathVariable("fileId") String fileId, HttpServletRequest request) {
BaseUser baseUser = getBaseUser(request);
if (baseUser == null) {
return Response.bad("未获取到用户信息");
}
String _w_filepath = URLDecoder.decode(request.getHeader("X-User-Query"), "UTF-8");
// 校验文件参数
if(StringUtils.isEmptyWithTrim(_w_filepath)){
return Response.bad("未获取到文件路径信息");
}
// 校验文件地址
String regex = "url=([^&]+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(_w_filepath);
if (matcher.find()) {
String url = matcher.group(1);
String _w_existMark = "1";
try {
Map<String, Object> map = fileService.getWebFilesInfo(url, _w_existMark, fileId, baseUser);
return Response.success(map);
} catch (Exception e) {
return Response.bad("获取文件元数据异常");
}
} else {
return Response.bad("获取文件元数据异常");
}
}
public interface WpsFlieService {
Map<String, Object> getWebFilesInfo(String filePath, String fileId, String existMark, BaseUser user);
}
/**
* 获取文件元数据(WPS 新版本)
* @param filePath 文件路径
* @return
*/
public Map<String, Object> getWebFilesInfo(String filePath, String existMark, String fileId, BaseUser baseUser){
// 构建默认user信息
WpsUser wpsUser = new WpsUser(
baseUser.getUserId(), baseUser.getUserRealName(), "read", " ");
int fileSize = Math.abs(FileUtil.getFileSize(filePath));
WpsWatermark mark = new WpsWatermark();
mark.setValue(baseUser.getUserRealName()+"\n"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
if (!Objects.equals("1", existMark)) {
mark.setType(0);
}
String fileNameWithExtension = filePath.substring(filePath.lastIndexOf('/') + 1);
String fileID = fileNameWithExtension.substring(0, fileNameWithExtension.lastIndexOf('.'));
// 构建文件
WpsFile file = new WpsFile(
fileID, FileUtil.getFileName(filePath),
1, fileSize, "1", System.currentTimeMillis()
);
return new HashMap<String, Object>() {
{
put("data", file);
}
};
}
WPS下载校验接口
@GetMapping("/v3/3rd/files/{fileId}/download")
@Log(title = "WPS下载校验接口")
public ResponseEntity<Object> getDownloadInfo(@PathVariable("fileId") String fileId, HttpServletRequest request) {
BaseUser baseUser = getBaseUser(request);
if (baseUser == null) {
return Response.bad("未获取到用户信息");
}
String _w_filepath = URLDecoder.decode(request.getHeader("X-User-Query"), "UTF-8");
// 校验文件参数
if(StringUtils.isEmptyWithTrim(_w_filepath)){
return Response.bad("未获取到文件路径信息");
}
// 校验文件地址
String regex = "url=([^&]+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(_w_filepath);
if (matcher.find()) {
String url = matcher.group(1);
try {
Map<String, Object> map = new HashMap<String, Object>() {
{
put("data", new HashMap<String, Object>() {
{
put("url", url);
}
});
}
};
return Response.success(map);
} catch (Exception e) {
return Response.bad("获取文件元数据异常");
}
} else {
return Response.bad("获取文件元数据异常");
}
}
WPS权限鉴权校验接口
@GetMapping("/v3/3rd/files/{fileId}/permission")
@Log(title = "WPS权限鉴权校验接口")
public ResponseEntity<Object> getPermissionInfo(@PathVariable("fileId") String fileId, HttpServletRequest request) {
BaseUser baseUser = getBaseUser(request);
if (baseUser == null) {
return Response.bad("未获取到用户信息");
}
String _w_filepath = URLDecoder.decode(request.getHeader("X-User-Query"), "UTF-8");
try {
Map<String, Object> map = new HashMap<String, Object>() {
{
put("data", new HashMap<String, Object>() {
{
put("comment", 0);
put("copy", 0);
put("download", 0);
put("history", 0);
put("print", 0);
put("read", 1);
put("rename", 0);
put("saveas", 0);
put("update", 0);
put("comment", 0);
}
});
}
};
return Response.success(map);
} catch (Exception e) {
return Response.bad("获取文件元数据异常");
}
}
返回结构类
public class Response {
// keys
private final static String MSG_KEY = "msg";
private final static String STATUS_KEY = "status";
private final static String CODE_KEY = "code";
private final static String DATA_KEY = "data";
// msg
private final static String SUCCESS_MSG = "ok";
// value
private final static String SUCCESS_VALUE = "success";
/**
* 请求成功,并返回请求结果集
*
* @param data 返回到客户端的对象
* @return Spring mvc ResponseEntity
*/
public static ResponseEntity<Object> success(Map<String, Object> data, String msg) {
return getObjectResponseEntity(data, msg);
}
@SuppressWarnings("serial")
public static ResponseEntity<Object> success(String msg) {
Map<String, Object> result = new HashMap<String, Object>() {
{
put(STATUS_KEY, SUCCESS_VALUE);
put(MSG_KEY, msg);
put(CODE_KEY, 0);
}
};
return getEntity(result);
}
@SuppressWarnings("serial")
public static ResponseEntity<Object> success() {
Map<String, Object> result = new HashMap<String, Object>() {
{
put(STATUS_KEY, SUCCESS_VALUE);
put(MSG_KEY, SUCCESS_MSG);
put(CODE_KEY, 0);
}
};
return getEntity(result);
}
/**
* 请求成功,并返回请求结果集
*
* @param data 返回到客户端的对象
* @return Spring mvc ResponseEntity
*/
public static ResponseEntity<Object> success(Map<String, Object> data) {
return getObjectResponseEntity(data, SUCCESS_MSG);
}
@SuppressWarnings("serial")
public static ResponseEntity<Object> success(Object data) {
Map<String, Object> result = new HashMap<String, Object>() {
{
put(STATUS_KEY, SUCCESS_VALUE);
put(MSG_KEY, SUCCESS_MSG);
put(CODE_KEY, 0);
put(DATA_KEY, data);
}
};
return getEntity(result);
}
@SuppressWarnings("serial")
public static ResponseEntity<Object> success(boolean data, String msg) {
Map<String, Object> result = new HashMap<String, Object>() {
{
put(STATUS_KEY, SUCCESS_VALUE);
put(MSG_KEY, msg);
put(CODE_KEY, 0);
put(DATA_KEY, data);
}
};
return getEntity(result);
}
@SuppressWarnings("serial")
public static ResponseEntity<Object> bad(String msg) {
Map<String, Object> result = new HashMap<String, Object>() {
{
put(STATUS_KEY, SUCCESS_VALUE);
put(MSG_KEY, msg);
put(CODE_KEY, HttpStatus.BAD_REQUEST.value());
}
};
return getEntity(result);
}
@SuppressWarnings("serial")
private static ResponseEntity<Object> getObjectResponseEntity(Map<String, Object> data, String msg) {
Map<String, Object> result = new HashMap<String, Object>() {
{
// put(STATUS_KEY, SUCCESS_VALUE);
// put(MSG_KEY, msg);
put(CODE_KEY, 0);
for (Map.Entry<String, Object> entry : data.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
};
return getEntity(result);
}
@SuppressWarnings("serial")
private static ResponseEntity<Object> getEntity(Object body) {
List<String> contentType = new ArrayList<String>() {
{
add("application/json;charset=utf-8");
}
};
MultiValueMap<String, String> headers = new HttpHeaders() {
{
put("Content-Type", contentType);
}
};
return new ResponseEntity<>(body, headers, HttpStatus.OK);
}
}
三、开发篇(前端)
1、引入WPS依赖库;
下载地址:JSSDK。
因为当前开发系统是使用Vue2的技术栈,所以需使用Es结构的数据进行引入,如
import WebOfficeSDK from '@/utils/web-office-sdk-solution-v2.0.6.es.js'
我当前JS文件是在utils下的。
2、WebOfficeSDK初始化
openWps(url) {
console.log(' ---------------url------------------ ', url)
if(url){
var urlObj \= new URL(url);
var filepath \= urlObj.searchParams.get('\_w\_filepath');
console.log('输出 -------------' ,filepath); // 输出:
console.log(' getToken() -------------' , getToken()); // 输出:
if(this.isFilePath(filepath)){
var parts \= filepath.split('/');
var fileNameWithExtension \= parts\[parts.length \- 1\];
var fileName \= fileNameWithExtension.split('.')\[0\];
console.log(' ----------fileName-------- ', fileName)
console.log('输出 -------------' ,filepath); // 输出:
// 获取文件扩展名
var fileExtension \= filepath.split('.').pop();
var officeTypeStr \= '';
// 根据文件扩展名判断文件类型
if (fileExtension \=== 'txt' || fileExtension \=== 'doc' || fileExtension \=== 'docx') {
officeTypeStr \= WebOfficeSDK.OfficeType.Writer;
console.log("这是文字文件");
} else if (fileExtension \=== 'xls' || fileExtension \=== 'xlsx' || fileExtension \=== 'csv') {
officeTypeStr \= WebOfficeSDK.OfficeType.Spreadsheet;
console.log("这是表格文件");
} else if (fileExtension \=== 'ppt' || fileExtension \=== 'pptx') {
officeTypeStr \= WebOfficeSDK.OfficeType.Presentation;
console.log("这是演示文件");
} else if (fileExtension \=== 'pdf') {
officeTypeStr \= WebOfficeSDK.OfficeType.Pdf;
console.log("这是PDF文件");
} else {
console.log("未知文件类型");
}
if(officeTypeStr){
// new
WebOfficeSDK.init({
officeType: officeTypeStr,
appId: 'SX20240528THJUYB',
fileId: fileName,
mount: document.querySelector('#viewFile'),
token: getToken(),
customArgs: {
url: filepath
}
})
}
// old
// const wps = WPS.config({
// mode: 'simple',
// mount: document.querySelector('#viewFile'),
// wpsUrl: url,
// fileId: fileName,
// commonOptions: {
// isShowTopArea: false,
// isShowHeader: false,
// isIframeViewFullscreen: true,
// }
// });
// wps.setToken({ "token": getToken(),"timeout":600000});
}
}
},
isFilePath(url) {
try {
var urlObj \= new URL(url);
var pathname \= urlObj.pathname;
var fileName \= pathname.split('/').pop();
return fileName.includes('.') && fileName.lastIndexOf('.') !== 0;
} catch (e) {
return false; // 如果URL无效,返回false
}
},