分类 前端技术 下的文章

前端下载文件的几种方式

阶段性总结一下前端可以用于文件下载的几种方法

1. a[href=*] 右键另存为

最原始的方式之一,在很多远古的网站上会存在,一般需要搭配后端对文件请求的 reponseHeader 设置 Content-Disposition ,否则用户直接点击的话可能会打开预览而不是文件下载。

如果服务器实现了 Content-Disposition 的话,前端也可以使用这些方式直接去打开对应的url:

  • location.href
  • window.open
  • iframe[src]
  • a[href].click

2. a[download='filename'] 使用 download 属性

现代 HTML5 规范提供了一种直接在链接声明文件可下载的方式,使用 a 标签的 download 属性,属性值将会作为下载文件的文件名,使用这个方法,结合 ObjectUrl,首次将前端生成文件并直接在网页上触发下载变成了可能。

在这个API的基础上终于能够做到直接在前端生成并下载 文本文件、excel 等文件,而无需后端服务参与。

上述两种方式都有一个比较基础和典型的实现:https://github.com/eligrey/FileSaver.js

3. ServiceWorker

ServiceWorker 对于全局请求的劫持,结合 Stream API, 使前端流式创建/下载文件变成了可能,在前端技术栈上可以做到流式下载一个超大文件,例如几个 G 大小的视频。

一个比较经典的实现来自: https://github.com/jimmywarting/StreamSaver.js

4. showSaveFilePicker

Chrome>86 的才可以使用的一个API,同样可以用于保存文件,属于 浏览器 FileSystemApi 的一部分,同样可以流式写入,缺点是不会产生浏览器原生的下载进度和记录,需要自己实现下载进度的相关交互界面。

使用 SVG 实现 Canvas 响应式缩放

很多时候我们需要做一个响应式的页面,如果页面中存在一个Canvas,调整尺寸的时候一般就需要重绘它。

在大部分情况下,操作 Canvas 并重绘并非不可接受,但是在一些场景中,我们既需要保持高分辨率的 Canvas,又需要它能适应屏幕的宽度,比如我们创建了一个 Canvas 用于捕捉屏幕上的视频流或者图片流,并且可能启动了一个 MediaRecorder 录制该 Canvas,在这个时候,强行修改Canvas的尺寸会影响正在录制的 MediaRecorder ,即便不在录制过程中,也会降低画面的分辨率,那么有什么其他方法吗?

做法一

使用一个新的 Canvas 来进行渲染,与原本的录制 Canvas 隔离。

我们可以将录制或者相关的业务逻辑放在另一个 Canvas中,可能是 offscreenCanvas。
然后我们在 requestAnimationFrame 或类似的时机,将 offscreenCanvas的内容绘制到实际显示的 Canvas 中。

这个方法的优势在于整体的数据流向不会变,缺点是额外绘制一个 Canvas 总是一种性能损耗,并且对上屏的 Canvas,我们依然要去监听 resize 并且重设 Canvas 的宽高。

做法二

仔细回顾一下我们的需求,如果要做到最完美的版本,即:我们需要有一个方案,他能够让 Canvas 全尺寸绘制并且不影响所有Canvas 相关的API,与此同时,它还要支持 CSS 的响应式逻辑。

没错,SVG。

SVG能够在 DOM 中创造一块独立的渲染逻辑,其中由 SVG 标签的 viewBOX 定义了整个渲染区域的坐标和尺寸,而在 DOM 中,它就像一个图片一样,可以使用 object-fit 或者 width 等 CSS 属性来实际控制它的尺寸和渲染方式。

于是在一个 Vue Template 中,我们可以这样写一个 Canvas:

<svg :viewBox="`0 0 ${loadedArea?.width} ${loadedArea?.height}`" xmlns="http://www.w3.org/2000/svg" class="w-full">
  <foreignObject x="0" y="0" :width="loadedArea?.width" :height="loadedArea?.height">
    <canvas ref="ImageCanvas" :height="loadedArea?.height" :width="loadedArea?.width" class="canvas"></canvas>
  </foreignObject>
</svg>
/src/tools/radar-chart/index.vue#L323-L328

使用 foreignObject ,并且把 SVG 的 viewbox 设成和 Canvas 相同的尺寸,就可以使 Canvas 完全铺满 SVG 的渲染空间。

最后,原有的逻辑完全不需要修改,依然可以通过 JS 去操作 Canvas的 API ,可以说是一种比较完美的解决方法。

VSCode 插件记录

在除了语言专用的插件 和 LanguageService 之外,还有很多非常方便的插件:

  1. file-size 在状态栏看到文件大小
  2. indent-rainbow 用不同的彩虹颜色来标记缩进
  3. TODO-Highlight 高亮指定的关键字 “ TODO / Console ”等,方便快速在源码中定位 TODO
  4. es6-string-* 在模版字符串中解析另一种语法,用于拼接一些其他语法的字符串,这是一类插件,包括这些:

    • es6-string-css
    • es6-string-html
    • es6-string-javascript
    • inline-sql
  5. Gitless 是 Gitlens 的 Fork 品,不含付费项目
  6. CodeGeeX 类似于 Github Copilot 的代码辅助工具,不聪明也不太蠢,至少免费

部署 hackChat 并在 Nginx 同时提供 http 和 websocket 服务

主要重点在于单端口并且同一个路径提供不同服务,首先需要满足以下条件:

  1. 首先根据官方教程完成部署,并使用pm2 start ./server/main.js --node-args="-r esm" --name hackchat启动服务。
  2. 假设在创建配置时,默认选择的是 6060 端口作为 websocket 端口。
  3. 假设需要使用 once.chat 这个域名,但是只想使用 443 端口,不希望 websocket 走 6060。

流程:

  1. 修改 client/client.js,在 320 行找到 var wsPath = ':6060';改为 var wsPath = '/';
  2. 根据普通的 Nginx 配置流程,完成端口监听,SSL 等配置,我这边用的 BT面板,就不再详细说了。
  3. 修改 Nginx 配置,对于 localtion / 进行如下配置

    location / {
         root  /www/wwwroot/hackchat/client;
         if ( $http_upgrade = "websocket" ) {
           proxy_pass http://127.0.0.1:6060;
             }
           proxy_set_header Host $host:$server_port;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header REMOTE-HOST $remote_addr;
           add_header X-Cache $upstream_cache_status;
           proxy_set_header X-Host $host:$server_port;
           proxy_set_header X-Scheme $scheme;
           proxy_connect_timeout 30s;
           proxy_read_timeout 86400s;
           proxy_send_timeout 30s;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";
     }

此时打开网页,即可正常加载静态资源的同时,也能用根路径连接到 websocket 服务。

如果嫌麻烦的话呢,也可以修改 wsPath ,带上 /chat-ws 这样的 path (官方的实现方式),在 Nginx 中就可以直接通过 location 进行区分了,配置起来更简单一点。

欢迎体验: once.chat