onlyoffice+vue集成

1、Document Server

53522可以改成你的端口(云服务器记得开权限),注意/var/lib/onlyoffice/documentserver/App_Data/cache/files

  • 注意上面的cache/files,是通过docker exec -it 3a3afa942911(这个是docker ps看到的container的id) bash
  • 然后用find / -name *.docx 这样搜索所有的docx格式的文件,找到的cache files,这里存的是修改后还未保存的file
    进入项目目录后,先创建docker volume:
mkdir -p app/onlyoffice/DocumentServer/logs
mkdir -p app/onlyoffice/DocumentServer/data
mkdir -p app/onlyoffice/DocumentServer/lib
mkdir -p app/onlyoffice/DocumentServer/cache/files
mkdir -p app/onlyoffice/DocumentServer/db

运行docker:

sudo docker run -i -t -d -p 53522:80 --restart=always \
    -v `pwd`/app/onlyoffice/DocumentServer/logs:/var/log/onlyoffice  \
    -v `pwd`/app/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data  \
    -v `pwd`/app/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice \
    -v `pwd`/app/onlyoffice/DocumentServer/cache/files:/var/lib/onlyoffice/documentserver/App_Data/cache/files \
    -v `pwd`/app/onlyoffice/DocumentServer/db:/var/lib/postgresql  onlyoffice/documentserver

2、maven 配置阿里云镜像

3、拉实例项目:

git clone https://github.com/ONLYOFFICE/document-server-integration.git
cd document-server-integration/web/documentserver-example/java-spring

4. 配置application.properties文件:

# server.address=127.0.0.1
# 8181防火墙开放是的
server.port=8181 
files.docservice.url.site=http://xxx.xxx.xxx.xxx/

5. 运行命令:

mvn clean
mvn spring-boot:run

集成:

vue-editor








vue-fView

使用前面的editor







后端

注意1:

如果用的是shiro,要放开权限
filterMap.put(“/onlyOffice/**”, “anon”);//从docker中请求后端没有带token,这里不放行会报错
filterMap.put(“/plan/previewFile”, “anon”);//从docker中请求后端没有带token,这里不放行会报错
filterMap.put(“/plan/saveOnlyOfficeFile”, “anon”);//从docker中请求后端没有带token,这里不放行会报错

注意2

在保存时,我遇到了403错误,访问docker中的cache/files失败,所以前面把cache/files暴露出来,访问docker中的cache/files转为访问本地的cache/files中对应的文件。

FileController

整理了一半有点累了…直接放controller。。。
后面再说完整的弄到一个项目吧。。。

本质上就是,从本地文件系统中下载文件(download接口)
保存接口则是:普通修改,status是1,socket一直建立连接。文档关闭后10s,会产生status为2的回调,走下面的track接口,从cache中获取output.docx,覆盖到之前的路径,也就是保存成功!
其它都是文件系统的操作
更多见官方的示例 https://github.com/ONLYOFFICE/document-server-integration.git

package com.ironpan.integration.modules.office.controller;

import com.alibaba.fastjson.JSONObject;
import com.ironpan.integration.modules.office.utils.DefaultFileUtility;
import com.ironpan.integration.modules.office.utils.FileStorageMutator;
import com.ironpan.integration.modules.office.utils.LocalFileStorage;
import com.ironpan.integration.modules.office.entity.R;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

@CrossOrigin("*")
@Controller
public class FileController {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private DefaultFileUtility fileUtility;
    @Autowired
    private FileStorageMutator storageMutator;

    @Autowired
    private LocalFileStorage localFileStorage;

    @Value("${cache.file.path}")
    private String cacheFilePath;

    @GetMapping(path = "/download")
    public ResponseEntity<Resource> download(HttpServletRequest request, @RequestParam("fileName") String fileName){
        try{
            logger.info("in /download");

            return downloadFile(fileName);
        } catch(Exception e){
            return null;
        }
    }

    @PostMapping("/upload")
    @ResponseBody
    public R upload(@RequestParam("file") MultipartFile file){
        try {
            if (file.isEmpty()) {
                return R.error("文件不能为空");
            }
            logger.info("开始文件上传");
            String fullFileName = file.getOriginalFilename();  // get file name
            String fileExtension = fileUtility.getFileExtension(fullFileName);  // get file extension
            long fileSize = file.getSize();  // get file size
            byte[] bytes = file.getBytes();  // get file in bytes

            // check if the file size exceeds the maximum file size or is less than 0
            if(fileUtility.getMaxFileSize() < fileSize || fileSize <= 0){
                return R.error("File size is incorrect");
            }

            // check if file extension is supported by the editor
            if(!fileUtility.getFileExts().contains(fileExtension)){
                return R.error("File type is not supported");
            }

            String fileNamePath = storageMutator.updateFile(fullFileName, bytes);  // update a file
            logger.info("       fileNamePath:{}, fullFileName:{}", fileNamePath, fullFileName);

            if (StringUtils.isBlank(fileNamePath)){
                throw new IOException("Could not update a file");  // if the file cannot be updated, an error occurs
            }

            fullFileName = fileUtility.getFileNameWithoutExtension(fileNamePath) + fileExtension;  // get full file name

            return R.ok().put("upload", createUserMetadata(fullFileName));  // 第一个参数uid, create user metadata and return it
        } catch (Exception e) {
            e.printStackTrace();
        }
        return R.error("Something went wrong when uploading the file");
    }

    @RequestMapping(value = "/track", method = RequestMethod.POST)
    public void saveOnlyOfficeFile(HttpServletRequest request, HttpServletResponse response,
        @RequestParam("fileName") String fileName) throws IOException, ServletException {
        String planPath = localFileStorage.getStorageLocation();
        PrintWriter writer = response.getWriter();
        String pathForSave = planPath + "/" + fileName;//这里应该是加上文件名,这样才能把修改后的文件覆盖上去
        Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
        String body = scanner.hasNext() ? scanner.next() : "{}";
        logger.info("接收的回调body参数为[{}]", body);
//        logger.info("接收的回调ReportProjectPlan参数为[{}]", JSON.toJSON(params));

        JSONObject jsonObj = JSONObject.parseObject(body);

        //普通修改,status是1,socket一直建立连接。文档关闭后10s,会产生status为2的回调,走下面,从cache中获取output.docx,覆盖到之前的路径,也就是保存成功
        if( jsonObj.getInteger("status") != null &&
            (jsonObj.getInteger("status") == 2 || jsonObj.getInteger("status") == 6))
        {
            String downloadUri = jsonObj.getString("url");
            logger.info("downloadUri: " + downloadUri);
//            URL url = new URL(downloadUri);
//            java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
            //java.io.IOException: Server returned HTTP response code: 403 for URL:

            //http://10.10.111.22:53522/ cache/files/ce2a410b5a7fdc8b224d_8816/output.docx/output.docx?md5=TbOcPEu8ZR3yKmljGQSwSA&expires=1650540479&filename=output.docx
            String[] split = downloadUri.split("/");
            int filesIndex = -1;
            for(int i = 0; i < split.length; i++){
                if(split[i].equals("files")) {
                    filesIndex = i;
                }
            }
            //替换成 this.cacheFilePath + ce2a410b5a7fdc8b224d_8816/output.docx
            String newPath = this.cacheFilePath + split[filesIndex + 1] + "/" +split[filesIndex + 2];
            Path filePath = Paths.get(newPath);
            URI uri = filePath.toUri();
            File file = new File(uri);
            logger.info("新的url:{}, File(uri).exists():{}", newPath, file.exists());
            InputStream stream = new FileInputStream(file);

//            InputStream stream = connection.getInputStream();

            File fileToSave = new File(pathForSave);
            if (!fileToSave.exists()) {   //文件不存在则创建文件,先创建目录
                File dir = new File(fileToSave.getParent());
                dir.mkdirs();
                fileToSave.createNewFile();
                logger.info("文件路径:{} 对应的文件不存在,先创建目录", pathForSave);
            }
            try (FileOutputStream out = new FileOutputStream(fileToSave)) {//默认覆盖
                int read;
                final byte[] bytes = new byte[1024];
                while ((read = stream.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }

                out.flush();
                logger.info("写入文件成功");
            }

//            connection.disconnect();
        }else{
            logger.info("不支持的status, jsonObj.getInteger(\"status\"): {}", jsonObj.getInteger("status"));
        }
        writer.write("{\"error\":0}");
    }

    @GetMapping(path = "/storageLocation")
    @ResponseBody
    public String getStorageLocation(){
        return localFileStorage.getStorageLocation();
    }

    // create user metadata //第一个参数:String uid,
    private String createUserMetadata(String fullFileName) {//Integer.parseInt(uid)
        String documentType = fileUtility.getDocumentType(fullFileName).toString().toLowerCase();  // get document type
        return "{ \"filename\": \"" + fullFileName + "\", \"documentType\": \"" + documentType + "\" }";
    }

    // download data from the specified file
    private ResponseEntity<Resource> downloadFile(String fileName) throws IOException {

        Resource resource = storageMutator.loadFileAsResource(fileName);  // load the specified file as a resource

        String contentType = "application/octet-stream";

        logger.info("fileName:" + fileName + " MediaType.parseMediaType(contentType):" + MediaType.parseMediaType(contentType)
            + ", HttpHeaders.CONTENT_DISPOSITION:" + HttpHeaders.CONTENT_DISPOSITION
            + ", headerValues:" + "attachment; filename=\"" + resource.getFilename() + "\""
            + ", resource.isFile:" + resource.isFile()
            + ", resource.getURI:" + resource.getURI()
            + ", resource.getURL:" + resource.getURL()
            + ", resource.getFilename:" + resource.getFilename()
        );

        // create a response with the content type, header and body with the file data
        return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType(contentType))
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
            .body(resource);
    }
}

使用

添加路由:

    {path: '/fileEditor/:id',  name: 'fileEditor', component: () => import('@/views/modules/training/fView.vue')},

使用:

预览


viewFile (fileInfo) {
      console.log(`文件信息:`, fileInfo);
      let routeUrl = this.$router.resolve({
        path: '/fileEditor/' + fileInfo.attachId,
        query: {
          id : this.fileId, fileName: fileInfo.attachTitle
        }
      });
      window.open(routeUrl.href,'_blank')
    }

参考链接:
先在服务器上用docker配置
How to integrate online editors into your own website on Java Spring
java+vue+onlyoffice的简单集成
Springboot Vue element-ui前后端分离实现onlyoffice在线协同编辑Demo
OnlyOffice - 在webpack项目的页面上展示 PowerPoint
How it works-Opening file

你可能感兴趣的:(vue,vue.js,前端,javascript,java)