找回密码
 立即注册

QQ登录

只需一步,快速开始

Winny

超级版主

130

主题

246

帖子

1536

积分

超级版主

Rank: 8Rank: 8

积分
1536
Winny
超级版主   /  发表于:2022-7-26 11:56  /   查看:2047  /  回复:2
本帖最后由 Winny 于 2022-7-28 17:26 编辑

需求背景:在本地使用Excel时,经常会有需要在Excel中添加一些附件文件的需求,例如在Excel中附带一些Word,CAD图等等。Excel能支持这些文件的添加,本质上并不是Excel可以直接打开这些文件。而是由于我们本机已经安装了能打开这些文件的软件。关于Excel中上传附件文件可以参考:在Excel电子表格中插入对象
类比到Web端,也会有不少客户,希望能够在Spread中添加一个附件。但是Web端不必本地环境,无法直接打开Word等其它附件。但是在Excel中,也有一种通过链接的形式来显示附件的方式。而SpreadJS中也是提供超链接的形式,利用这一点,或许可以对SpreadJS添加附件展开探索。

解决方案:
SpreadJS中提供了三种超链接的形式,分别是:
(1)超链接单元格
(2)超链接类型
(3)HYPERLINK函数
超链接类型与HYPERLINK函数与Excel兼容,而超链接单元格从SpreadJS导出Excel时,由于实现机制有差异,Excel无法识别,所以实现附件需求尽量使用超链接类型或HYPERLINK函数,我在这里使用的是超链接类型。

在需要在某个单元格中上传附件时,我们可以弹出一个模态框,在模态框中上传文件,点击提交之后,可以对文件做一个暂存,将文件信息存储在单元格的Tag中。Tag在SpreadJS当中可以理解为是对单元格的一种属性标记,可以支持复杂类型。在这里我存储的是一个json,json结构如下:
  1. {
  2.     type: 'attachfile',
  3.     fileInfo: object file
  4. }
复制代码
其中type表示当前是否为附件,fileInfo代表的是文件对象。由于我演示的是纯前端的demo,所以fileInfo中传递的是File对象,File对象在实际使用时需要从os中在我读取文件,这种形式在传前端的demo演示下可行。但在实际项目中,fileInfo应该使用的是文件上传完成后的路径地址,这一点在实际项目中一定要注意!同时需要将单元格设置为超链接,超链接的url为一个相对路径。至于为何是相对路径,这一点是为了后续导出文件后能正常访问到附件文件,与Excel中的机制相同。对同一个单元格重复上传附件时会做文件的替换,此时我们只需要对应的去修改Tag及超链接的信息即可。与此对应,清除附件文件,只需要清除对应的Tag和值信息即可。接下来会将一些核心的代码逻辑。

1. 创建页面的HTML结构
  1. <button id="uploadAttach">上传附件</button>
  2.     <div id="fileOperate" style="visibility: hidden;position: absolute;top: 100px;left: 300px;z-index: 10; background-color: #eee;padding: 16px">
  3.         <label for="choseFile">选择文件</label>
  4.         <input type="file" id="choseFile" name="choseFile"/>
  5.             <button id="submit">提交</button>
  6.             <button id="cancel">取消</button>
  7.     </div>
  8.     <div id="ss" style="width:100%;height:98vh;border:1px solid darkgray"></div>
复制代码
点击上传附件文件,id为fileOperate的div会显示出来,在这里可以完成文件的上传及暂存。

2. 暂存附件信息
  1. function hasAttachFile(sheet,row,col,file){
  2.     /**
  3.      * 附件文件暂存
  4.      * 这里由于没有服务端,所以我直接存了File对象,但File对象只有在实际使用时才会去获取实际的文件内容。在demo中可行
  5.      * 在实际项目中,需要将file对象上传到文件服务器中
  6.      * 上传完成后tag中的fileInfo应该代表的是文件的访问地址,而不能再是File对象。
  7.      */
  8.     sheet.setValue(row,col,file.name)
  9.     sheet.setTag(row,col,{
  10.         type: hyerlinkType,
  11.         fileInfo: file        // 实际项目中fileInfo应该为上传完成文件的访问路径
  12.     })
  13.     sheet.setHyperlink(row, col, {
  14.         url: file.name,
  15.         linkColor: '#0066cc',
  16.         visitedLinkColor: '#3399ff',
  17.         drawUnderline: true,
  18.         command:'downloadAttachFile',
  19.      }, GC.Spread.Sheets.SheetArea.viewport);
  20. }
复制代码
这一步起始主要用来设置文件上传之后单元格超链接及tag信息。细心的同学会注意到,这里我注册了一个命令,超链接本身会有一个跳转的行为,写command之后,会阻止这个默认跳转,转去执行对应的命令。注册的命令主要就是用来做附件文件的下载。关于SpreadJS中如何注册命令可以在学习指南或API中检索。
  1. // 下载文件
  2.     spread.commandManager().register("downloadAttachFile",{
  3.         canUndo: false,
  4.         execute: function(context,options,isUndo){
  5.             let sheet = context.getActiveSheet()
  6.             let row = sheet.getActiveRowIndex()
  7.             let col = sheet.getActiveColumnIndex()
  8.             let cellTag = sheet.getTag(row,col)
  9.             console.log(sheet,row,col,cellTag)
  10.             if(cellTag && cellTag.type==hyerlinkType){
  11.                 /***
  12.                  * 纯前端demo,文件存在于本地,fileInfo中存储的是File对象,可以直接获取到文件
  13.                  * 实际项目中,fileInfo应该是上传到文件服务器上的文件访问地址。
  14.                  * 因此这里需要发送请求,先获取文件blob,将获取的blob传递到saveAs的第二个参数中。
  15.                  */
  16.                 saveAs(cellTag.fileInfo,cellTag.fileInfo.name)
  17.             }
  18.         }
  19.     })
复制代码
在这里,我引入了三方组件库FileSaver,在点击超链接单元格时,可以支持当前附件文件的下载。

3. 附件文件清除
  1. document.getElementById("removeAttach").onclick = function(){
  2.     /***
  3.      * 清除附件
  4.      * 清除附件需要先删除远程文件服务器的文件,之后清除单元格的Tag信息。
  5.      * 这里前端演示demo,只删除了tag。
  6.      * 实际项目中tag中的fileInfo应该是文件上传后的路径
  7.      */
  8.     let sheet = spread.getActiveSheet()
  9.     let row = sheet.getActiveRowIndex()
  10.     let col = sheet.getActiveColumnIndex()
  11.      spread.commandManager().execute({
  12.             cmd:"removeAttachFile",
  13.             sheet,row,col
  14.         })
  15. }
复制代码

  4. 文件保存

SpreadJS提供了一种结构叫做SSJSON,用来描述一个Excel文件的全部信息。也提供了文件blob的形式,但由于导出文件流之后Tag信息会清除,因为Excel中并没有Tag的概念。所以对于当前的形式,需要将文件保存成json结构。如果有必须保存excel文件的需求,我们可以将文件信息保存在Excel与SpreadJS都能兼容的属性中。
  1. document.getElementById("fileSaver").onclick = function(){
  2.     // 保存文件
  3.     submitFile = spread.toJSON()
  4.     spread.clearSheets()
  5.     spread.addSheet(0)
  6. }
复制代码
因为是一个纯前端的demo,这里我用一个全局变量来表示当前保存的文件。


5. 文件加载
  1. document.getElementById("loadSubmitFile").onclick = function(){
  2.     // 加载已保存文件
  3.     spread.fromJSON(submitFile)
  4. }
复制代码


详细的Demo可以参考:SpreadJS上传附件

2 个回复

倒序浏览
fawinell
注册会员   /  发表于:2023-4-7 17:27:45
沙发
你好,你这个demo上传一个是没问题的,但是如果有下拉、复制操作,则复制的单元格是无法下载的,请问这个可以解决吗?

image.png821414489.png
回复 使用道具 举报
Richard.Ma讲师达人认证 悬赏达人认证 SpreadJS 开发认证
超级版主   /  发表于:2023-4-7 20:04:38
板凳
周一验证后给你回复,在jscode中似乎tag并没有被复制过去
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 立即注册
返回顶部