基于WPS的在线编辑服务【.net Core 3.1】

很多时候大家都需要用到在线文档编辑服务之类的东西,今天就以WPS的在线编辑为例。【目前仅支持以企业形式申请】

链接WPS开放平台。

效果图如下

基于WPS的在线编辑服务【.net Core 3.1】_第1张图片

 用到示例https://github.com/guolaopi/WPSOnlineDemo

 改过之后的后台代码示例:

[Route("api/[controller]/[Action]")]
    [ApiController]
    public class WpsController : ControllerBase
    {
        private ILogger _logger;
        private SessionManager _sessionManager;
        private IWebHostEnvironment _webHostEnvironment;
        private readonly UserService _userService;
        public WpsController(ILogger logger, SessionManager sessionManager, IWebHostEnvironment webHostEnvironment, UserService userService)
        {
            _logger = logger;
            _sessionManager = sessionManager;
            _webHostEnvironment = webHostEnvironment;
            _userService = userService;
        }

        /// 
        /// 简单过滤一下HttpRequest参数
        /// 
        /// 
        /// 
        private RequestParam FilterRequestForWPS(HttpRequest Request)
        {
            var result = new RequestParam();
            result.FileId = Request.Headers["x-weboffice-file-id"].ToString();
            var queryStr = Request.QueryString.ToString();
            queryStr = queryStr.StartsWith("?") ? queryStr.Substring(1) : queryStr;
            if (string.IsNullOrEmpty(queryStr) || string.IsNullOrEmpty(result.FileId))
            {
                return new RequestParam
                {
                    code = 403,
                    msg = "参数错误,无法打开文件",
                    Status = false
                };
            }

            // url参数序列化成Dictionary
            result.Params = queryStr.Split("&", StringSplitOptions.RemoveEmptyEntries).ToDictionary(p => p.Split("=")[0], p => p.Split("=")[1]);

            // 此处判断是否传递了自定义的 _w_userId 参数,如果不需要此参数的话可以注释该判断
            if (!result.Params.ContainsKey("_w_userId"))
            {
                return new RequestParam
                {
                    code = 403,
                    msg = "用户异常",
                    Status = false
                };
            }
            return result;
        }

        /// 
        /// 生成iframe用的url(此方法非WPS官方的,主要是为了签名,也可以自己实现)
        /// 
        /// 
        /// 
        [Route("/api/wps/genarate")]
        [HttpPost]
        [Authorize]
        [RoleAuthorize]
        public Task GenarateWPSUrl(GenarateRequest request)
        {
            try
            {
                //请求文件是否存在
                if (string.IsNullOrEmpty(request.FileName) || !System.IO.File.Exists($"{_webHostEnvironment.WebRootPath}/{request.FilePath}/{request.FileName}"))
                    return Task.Run(() =>
                    {
                        return new GenarateResult { Url = null };
                    });
                else
                {
                    return Task.Run(() =>
                    {
                        var url = WPSSignatureHelper.GenarateUrl(request.FileId,
                                                        request.FileType,
                                                        new Dictionary {
                                                    { "_w_userId", _sessionManager.CurrUser.Id.ToString() },
                                                    { "_w_fileName", request.FileName },
                                                    { "_w_readOnly", request.ReadOnly },
                                                    { "_w_filePath", request.FilePath }
                                                        });
                        // 上面的写法是在生成的url中带了两个自定义参数 _w_userId 和 _w_fileName,可以根据业务自己扩展,生成url是这样的:
                        // https://wwo.wps.cn/office/w/123?_w_appid=123456&_w_fileName=x.docx&_w_userId=5024&_w_signature=xxxxx

                        _logger.LogInformation($"生成URL:{url}");
                        // 也可以不写自定义参数,这样生成的url会只有 _w_appId 和 _w_ signatrue,例如:https://wwo.wps.cn/office/w/123?_w_appid=123456&_w_signature=xxxxx
                        //var url = WPSHelper.GenarateUrl(request.FileId,request.FileType);
                        return new GenarateResult { Url = url };
                    });
                }
            }
            catch (Exception c)
            {
                _logger.LogError(c, "签名Url出错");
                return Task.Run(() =>
                {
                    return new GenarateResult { Url = null };
                });
            }
        }

        #region WPS官方要求实现的回调接口

        /// 
        /// 获取文件元数据
        /// 
        /// 
        [Route("/v1/3rd/file/info")]
        [HttpGet]
        public FileInfoResult FileInfo()
        {
            // 简单的过滤下不合理的请求,可注释,以下接口基本上都有此过滤
            try
            {
                var request = FilterRequestForWPS(Request);
                if (!request.Status)
                {
                    return new FileInfoResult { code = request.code, msg = request.msg };
                }

                // 获取自定义参数
                var userId = request.Params["_w_userId"].ToString();
                var fileName = HttpUtility.UrlDecode(request.Params["_w_fileName"].ToString());
                var filePath = $"{_webHostEnvironment.WebRootPath}/{request.Params["_w_filePath"]}";
                var readOnly = bool.Parse(request.Params["_w_readOnly"].ToString());

                // 从数据库查询用户名、文件 等信息......
                var files = new FileInfo($"{filePath}/{fileName}");//文件信息
                var _user = _userService.GetSessionUserAsync(int.Parse(userId)).Result;

                // 创建时间和修改时间默认全是现在,可更改,但是注意时间戳是11位的(秒)
                //var now = TimestampHelper.GetCurrentTimestamp();
                var downLoadUrl = $"{WPSSignatureHelper.AppRequest}/{request.Params["_w_filePath"]}/{fileName}";

                var result = new FileInfoResult
                {
                    file = new WPSFile
                    {
                        id = request.FileId,
                        name = files.Name,
                        version = 1,
                        size = int.Parse(files.Length.ToString()), // WPS单位是B
                        create_time = TimestampHelper.GetCurrentTimestamp(DateTime.Parse(files.CreationTime.ToString())),
                        creator = _user.NickName,
                        modify_time = TimestampHelper.GetCurrentTimestamp(DateTime.Parse(files.LastWriteTime.ToString())),
                        modifier = _user.NickName,
                        download_url = downLoadUrl,
                        user_acl = new User_acl
                        {
                            history = 1, // 允许查看历史版本
                            rename = 1, // 允许重命名
                            copy = 1 // 允许复制
                        },
                        watermark = new Watermark
                        {
                            type = 0, // 1为有水印
                            value = "水印文字"
                        }
                    },
                    user = new UserForFile()
                    {
                        id = userId,
                        name = _user.NickName,
                        permission = readOnly ? "read" : "write", // write为允许编辑,read为只能查看
                        avatar_url = "https://blogadmin.changqingmao.com/upload/95d5ba28-f627-437d-935a-1e51b433816b.jpg",//头像url
                    }
                };
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "异常");
                return new FileInfoResult { code = 400, msg = ex.Message };
            }
        }

        /// 
        /// 获取用户信息
        /// 
        /// 包含一个名为ids的字符串数组,里面是用户id
        /// 
        [Route("/v1/3rd/user/info")]
        [HttpPost]
        public Task GetUserInfo(GetUserInfoRequest body)
        {
            return Task.Run(() =>
            {
                var request = FilterRequestForWPS(Request);
                if (!request.Status)
                {
                    return new UserModel();
                }
                var userId = request.Params["_w_userId"].ToString();
                var _user = _userService.GetSessionUserAsync(int.Parse(userId)).Result;
                var result = new UserModel
                {
                    id = _user.Id.ToString(),
                    name = _user.NickName,
                    avatar_url = "https://blogadmin.changqingmao.com/upload/95d5ba28-f627-437d-935a-1e51b433816b.jpg"
                };
                return result;
            });
        }

        /// 
        /// 通知此文件目前有哪些人正在协作
        /// 
        /// 
        /// 
        [Route("/v1/3rd/file/online")]
        [HttpPost]
        public Task Online(GetUserInfoRequest body)
        {
            return Task.Run(() =>
            {
                var result = new WPSBaseModel
                {
                    code = 200,
                    msg = "success"
                };
                return result;
            });
        }

        /// 
        /// 上传文件新版本(保存文件)
        /// 
        /// 传来的文件流
        /// 
        [Route("/v1/3rd/file/save")]
        [HttpPost]
        public Task SaveFile(IFormFile file)
        {
            return Task.Run(async () =>
            {
                try
                {
                    var request = FilterRequestForWPS(Request);
                    if (!request.Status)
                    {
                        return new SaveFileResult { code = request.code, msg = request.msg };
                    }

                    //保存的文件名
                    using (var stream = System.IO.File.Create($"{_webHostEnvironment.WebRootPath}/{request.Params["_w_filePath"]}/{HttpUtility.UrlDecode(request.Params["_w_fileName"])}"))
                    {
                        await file.CopyToAsync(stream);
                    }

                    var result = new SaveFileResult
                    {
                        file = new WPSFileModel
                        {
                            download_url = $"{WPSSignatureHelper.AppRequest}/{request.Params["_w_filePath"]}/{request.Params["_w_fileName"]}",
                            id = request.FileId,
                            name = request.Params["_w_fileName"].ToString()
                        }
                    };
                    return result;
                }
                catch (Exception ex)
                {
                    _logger.LogError("save file failed: ", ex);
                    return new SaveFileResult { code = 403, msg = "保存出现异常" };
                }
            });
        }

        /// 
        /// 获取特定版本的文件信息
        /// 
        /// 版本号
        /// 
        [Route("/v1/3rd/file/version")]
        [HttpGet]
        public Task Version(int version)
        {
            return Task.Run(() =>
            {
                var request = FilterRequestForWPS(Request);
                if (!request.Status)
                {
                    return new GetFileByVersionResult { code = request.code, msg = request.msg };
                }
                //获取参数
                var userId = request.Params["_w_userId"].ToString();
                var fileName = HttpUtility.UrlDecode(request.Params["_w_fileName"].ToString());
                var filePath = $"{_webHostEnvironment.WebRootPath}/{request.Params["_w_filePath"]}";
                // 从数据库查询文件信息......
                var files = new FileInfo($"{filePath}/{fileName}");
                var _user = _userService.GetSessionUserAsync(int.Parse(userId)).Result;
                // 创建时间和修改时间默认全是现在
                //var now = TimestampHelper.GetCurrentTimestamp();
                var result = new GetFileByVersionResult
                {
                    id = request.FileId,
                    name = fileName,
                    version = 1,
                    size = int.Parse(files.Length.ToString()),
                    create_time = TimestampHelper.GetCurrentTimestamp(DateTime.Parse(files.CreationTime.ToString())),
                    creator = _user.NickName,
                    modify_time = TimestampHelper.GetCurrentTimestamp(DateTime.Parse(files.LastWriteTime.ToString())),
                    modifier = _user.NickName,
                    download_url = $"{WPSSignatureHelper.AppRequest}/{request.Params["_w_filePath"]}/{fileName}"
                };
                return result;
            });
        }

        /// 
        /// 文件重命名
        /// 
        /// 包含一个name的字符串属性,值为保存的新文件名
        /// 
        [Route("/v1/3rd/file/rename")]
        [HttpPut]
        public Task RenameFile(RenameFileRequest body)
        {
            return Task.Run(() =>
            {
                var request = FilterRequestForWPS(Request);
                if (!request.Status)
                {
                    return new WPSBaseModel { code = request.code, msg = request.msg };
                }

                var result = new WPSBaseModel
                {
                    code = 200,
                    msg = "success"
                };
                return result;
            });
        }

        /// 
        /// 获取所有历史版本文件信息
        /// 
        /// 
        /// 
        [Route("/v1/3rd/file/history")]
        [HttpPost]
        public Task GetHistory(GetHistoryRequest body)
        {
            return Task.Run(() =>
            {
                var request = FilterRequestForWPS(Request);
                if (!request.Status)
                {
                    return new GetHistoryResult { code = request.code, msg = request.msg };
                }
                //获取参数
                var userId = request.Params["_w_userId"].ToString();
                var fileName = HttpUtility.UrlDecode(request.Params["_w_fileName"].ToString());
                var filePath = $"{_webHostEnvironment.WebRootPath}/{request.Params["_w_filePath"]}";
                // 从数据库查询文件信息......
                var files = new FileInfo($"{filePath}/{fileName}");
                // 创建时间和修改时间默认全是现在
                //var now = TimestampHelper.GetCurrentTimestamp();
                var _user = _userService.GetSessionUserAsync(int.Parse(userId)).Result;
                // 不需要使用历史版本功能的此处也请返回,如果此接口不通时,文档加载会报错:“GetFileInfoFailed”
                var result = new GetHistoryResult
                {
                    histories = new List
                    {
                        new HistroyModel
                        {
                         id=request.FileId,
                         name=fileName,
                         size=int.Parse(files.Length.ToString()), // 单位B
                         version=1,
                         download_url = $"{WPSSignatureHelper.AppRequest}/{request.Params["_w_filePath"]}/{fileName}",
                         create_time=TimestampHelper.GetCurrentTimestamp(DateTime.Parse(files.CreationTime.ToString())),
                         modify_time=TimestampHelper.GetCurrentTimestamp(DateTime.Parse(files.LastWriteTime.ToString())),
                         creator=new UserModel
                         {
                             id=_user.Id.ToString(),
                             name=_user.NickName,
                             avatar_url = "https://blogadmin.changqingmao.com/upload/95d5ba28-f627-437d-935a-1e51b433816b.jpg"
                         },
                         modifier=new UserModel
                         {
                             id=_user.Id.ToString(),
                             name=_user.NickName,
                             avatar_url = "https://blogadmin.changqingmao.com/upload/95d5ba28-f627-437d-935a-1e51b433816b.jpg"
                         }
                        }
                    }
                };
                return result;
            });
        }

        /// 
        /// 新建文件
        /// 
        /// 
        [Route("/v1/3rd/file/new")]
        [HttpPost]
        public Task NewFile(CreateWPSFileRequest request)
        {
            return Task.Run(async () =>
            {
                try
                {
                    var filterRequest = FilterRequestForWPS(Request);
                    if (!filterRequest.Status)
                    {
                        return new CreateWPSFileResult { code = filterRequest.code, msg = filterRequest.msg };
                    }
                    var userId = filterRequest.Params["_w_userId"].ToString();
                    var _user = _userService.GetSessionUserAsync(int.Parse(userId)).Result;

                    using (var stream = System.IO.File.Create($"{filterRequest.Params["_w_fileName"]}"))
                    {
                        await request.file.CopyToAsync(stream);
                    }

                    var result = new CreateWPSFileResult
                    {
                        redirect_url = $"{WPSSignatureHelper.AppRequest}/{filterRequest.Params["_w_filePath"]}",
                        user_id = _user.Id.ToString()
                    };
                    return result;
                }
                catch (Exception ex)
                {
                    _logger.LogError("save file failed: ", ex);
                    return new CreateWPSFileResult { code = 403, msg = "新建文件出现异常" };
                }
            });
        }

        /// 
        /// 回调通知
        /// 
        /// 
        /// 
        [Route("/v1/3rd/onnotify")]
        [HttpPost]
        public Task WPSNotify(WPSNotifyRequest body)
        {
            return Task.Run(() =>
            {
                var result = new WPSBaseModel
                {
                    code = 200,
                    msg = "success"
                };
                return result;
            });
        }

        #endregion
    }

Model参见上述Github的model。

todo:建议将GenarateWPSUrl进行拦截,比如你的权限认证等。回调方法里面参数需要自行请求数据库或者读取指定文件夹文件(比如用户信息或者文件信息)。

我是直接读取的文件夹,所以将文件名Url解码。进行一系列操作

StartUp方法:

// 配置WPS的 AppId 和 AppSecretKey
            WPSSignatureHelper.Config(Configuration["WPSConfig:AppId"], Configuration["WPSConfig:AppSecretKey"], Configuration["WPSConfig:AppRequest"]);
            //wps跨域
            services.AddCors(p =>
            {
                p.AddPolicy("wps", policy =>
                {
                    policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
                    //policy.AllowAnyMethod()
                    //.SetIsOriginAllowed(_ => true)
                    //.AllowAnyHeader()
                    //.AllowCredentials();
                });
            });


//
app.UseCors("wps");

todo:core 3.0及以上需参照我这样写,2.2请直接参照上述Github

js前端如下:

    $.getScript("/js/jwps.js");
    /**
     * 发起wps请求
     * @param {any} url
     * @param {any} type
     * @param {any} param
     * @param {any} async
     * @param {any} elem
     */
    function Ajax(url, type, param, async, elem) {
        return new Promise(function (resolve, reject) {
            $.ajax({
                url: url,
                contentType: "application/json",
                dataType: "json",
                type: type,
                data: JSON.stringify(param),
                async: async,
                success: res => {
                    var wpsUrl = res.url;
                    console.log(wpsUrl);
                    var wps = showWPS(wpsUrl, elem);
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    console.log(jqXHR.status);
                    if (jqXHR.status === 401) {
                    } else {
                    }
                }
            });
        });
    }

    /**
     * 
     * @param {any} url
     * @param {any} elem
     */
    function showWPS(url, elem) {
        // 初始化
        var wps = WPS.config({
            mount: document.querySelector(elem),
            // 文字
            wpsUrl: url,
            headers: {
                shareBtn: {
                    tooltip: "分享",
                    subscribe: function () {
                        console.log("click callback");
                    }
                }
            }
        });
        return wps;
    }


//调用

//类
       function Demo(fileId, fileName, fileType, readOnly, filePath) {
            this.fileId = fileId;
            this.fileName = fileName;
            this.fileType = fileType;
            this.readOnly = readOnly;
            this.filePath = filePath;
        }
Ajax('http://jcxt.xnxv.cn/api/wps/genarate', 'post', new Demo(fileId, fileName, 1, false, 'PactTemplate'), 'false', '#PoupHtml').then(res => {
                        console.log(res);
                    });

 

你可能感兴趣的:(.Net,Core,C#,.NET,MVC)