Richard.Huang 发表于 2023-9-21 14:22:06

利用SJS数据流实现前后端的文件传输

本帖最后由 Richard.Huang 于 2023-9-21 15:02 编辑

背景:SpreadJS作为一款纯前端的表格控件,并不具备服务端的存储能力,因此想要持久化编辑的文件,大家只能保存在本地,这对于很多办公需求苛刻的用户来说并不是很好,用户更希望的是,将表格内容保存在云端,需要的时候,打开页面直接加载云端内容即可,我们怎么实现这个想法呢?其实实现这个的方式有很多,例如之前大家阅读很多的通过数据流进行前后端传输的精华帖:SpreadJS + Vue + Axios 实现服务端加载、上传Excel,博主在帖子中提及的方案便是通过Excel文件的数据流来实现前后端的交互,数据流的方式不用落盘直接传输给后端保存,十分友好,但是Excel文件本身体积庞大,如果数据量也庞大,那么传输过程会因此变得缓慢。有没有更轻量的方式呢?答案是有的,SpreadJS在V16.2.2版本中提供了另一种新的.sjs的文件格式,该格式大大改善了非常大的Excel文件加载时间和内存使用SpreadJS自有文件格式,同时与以前的SpreadJS版本相比,重新保存后的文件大小大大减少
方法:SpreadJS提供了open方法和save方法对.sjs文件进行加载和保存,如果我们在文件落盘时拿到数据流,直接传输给后端,便不需要在前端进行落盘处理了,拿到数据流后直接传输给后端即可
[*]保存时拿取数据流传输给后端
spread.save(
       function (blob) {
                var xhr = new XMLHttpRequest();
                xhr.open("POST", "/sjs/add", true);

                var formData = new FormData();
                formData.append('sjs', blob);
                formData.append('fileName', inputValue);

                xhr.onreadystatechange = function () {
                        if (xhr.readyState === XMLHttpRequest.DONE) {
                              if (xhr.status === 200) {
                                        console.log(xhr.responseText);
                                        var result = JSON.parse(xhr.responseText);
                                        var code = result.code;

                                        if (code === 200) {
                                                console.log("保存成功")
                                        }
                              } else {
                                        console.log(xhr.status);
                                        console.log(xhr.responseText);
                              }
                        }
                };

                xhr.send(formData);
      },
      function (e) {
                console.log(e);
      }
);
[*]加载时请求数据流进行加载
var spread = GC.Spread.Sheets.findControl(document.getElementById("ss"));
xhr.open("get", "/sjs/get?fileName=" + inputValue)
xhr.responseType = "blob"
xhr.addEventListener("loadend", function () {
      if (this.readyState == 4 && this.status == 200) {
                let file = new File(, 'test.sjs')
                // 也可以封装成blob,spread.open()中直接传递blob即可
                // var blob = new Blob(, {type:'application/zip'});
                spread.open(file)
      }
})
xhr.send()
后端可以使用我们的GCExcel对数据进行落盘,具体是用时可以将落盘的路径保存在关系型数据库中,例如mysql中,路径保存我们不做介绍,我们将核心的落盘的controller层方法进行实践:1. 接收数据流并落盘@PostMapping("/add")
public R addSJS(@RequestParam("sjs") MultipartFile sjsFile, @RequestParam("fileName") String fileName) throws IOException {
      int userId = 4;
      // 获取数据
      String filePath = "data/" + userId + "/";
      File directory = new File(filePath);
      if (!directory.exists()) {
                boolean success = directory.mkdirs();
                if (success) {
                        System.out.println("文件夹创建成功!");
                } else {
                        System.out.println("文件夹创建失败!");
                }
      } else {
                System.out.println("文件夹已存在!");
      }

      // 创建一个新的文件流来保存文件。
      InputStream out = null;
      try {
                out = sjsFile.getInputStream();
      } catch (FileNotFoundException e) {
            // 处理文件未找到异常。
                e.printStackTrace();
      } catch (IOException e) {
                throw new RuntimeException(e);
      }

      // 保存流文件
      Workbook workbook = new Workbook();
      // 将Workbook对象保存为.sjs文件。
      workbook.open(out, OpenFileFormat.Sjs);
      workbook.save(filePath + fileName, SaveFileFormat.Sjs);
      // 一切正常
      return R.success(CodeEnum.SUCCESS,Constants.OK);
}2. 获取文件,并加载为数据流进行传输@RequestMapping(value = "/get", method = RequestMethod.GET)
public void downloadFile(@RequestParam("fileName") String fileName, HttpServletResponse response) throws IOException {
      String ssjsonPath = "data/4/" + fileName;
      File file = new File(ssjsonPath);

    // 设置响应内容类型为二进制流
      response.setContentType("application/octet-stream");
      response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
      response.setHeader("Content-Length", String.valueOf(file.length()));

    // 读取文件并写入响应的输出流
      InputStream inputStream = new FileInputStream(file);
      OutputStream outputStream = response.getOutputStream();
      byte[] buffer = new byte;
      int bytesRead;
      while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
      }
      inputStream.close();
      outputStream.close();
}详细代码可以参考附件工程,大家可以将附件下载到本地,使用ide或者其他代码编辑器打开这个maven工程,并加载相关依赖,然后启动DemoApplication.java这个启动项即可,以下是效果展示:
页: [1]
查看完整版本: 利用SJS数据流实现前后端的文件传输