【文件上传系列】No.0 利用 FormData 实现文件上传、监控网路速度和上传进度(原生前端,Koa 后端)
利用 FormData 实现文件上传
基础功能:上传文件
演示如下:

概括流程:
- 前端:把文件数据获取并
append到FormData对象中- 后端:通过
ctx.request.files对象拿到二进制数据,获得node暂存的文件路径
前端
前端的工作就是把页面写好,ajax 和 FormData 组装好,发送给后端。
基础功能:组装 FormData 和 XHR
前端这边代码如下:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><input type="file" name="file" id="file" /><button id="btn">点我上传</button></body><script>const btn = document.getElementById('btn');btn.onclick = function () {let file = document.querySelector('#file').files[0];console.log(file);// 组装好 formData// 文件传输是通过正文传输的,所以要用 postlet formData = new FormData(); // 这里的 new formData() 会自动帮我设置 content-typeformData.append('data', file);formData.append('name', '文件');formData.append('年龄', 20);// 组装好 xhrlet xhr = new XMLHttpRequest();xhr.open('post', '/upload');xhr.onload = function () {console.log(xhr.responseText);};xhr.send(formData);};</script>
</html>
基础:xhr.upload 上传钩子函数
大概有如下几个钩子(比较常用的)
xhr.upload.onprogress = (event) => {console.log('上传过程');
}
xhr.upload.onload = () => {console.log('上传成功');
}
xhr.upload.onloadend = () => {console.log('上传完成');
}
xhr.upload.onabort = () => {console.log('取消上传');
}
onprogress 这个函数是在上传过程中不断循环被执行的,其中有事件因子 event,里面会有上传中的信息
如果想要监控速度和进度的话,可以在上传的过程中计算出来
如果想要取消上传,就把 xhr.abort() 即可。
document.getElementById('cancelBtn').addEventListener('click', function () {// 取消上传xhr.abort();
});
基础:利用钩子函数计算下载速度和进度
速度:思路就是求出一段时间的下载量(
byte)和一段时间(s),然后做除法
s p e e d = d 单位数据包大小 b y t e d 单位时间 s b y t e / s speed = \frac{{\rm d}单位数据包大小 byte}{{\rm d }单位时间 s}{byte/s} speed=d单位时间sd单位数据包大小bytebyte/s
let oldDataSize;
let oldTime;
xhr.onload = function () {let responseText = xhr.responseText;console.log("上传成功", responseText);
};
xhr.upload.onloadstart = () => {console.log("上传开始!");oldLoaded = 0;oldTime = new Date().getTime();
};
xhr.upload.onprogress = (enent) => {// 计算单位时间文件加载大小let duringLoaded = event.loaded - oldLoaded;// 计算单位时间差let duringTime = (new Date().getTime() - oldTime) / 1000; // 时间戳,默认单位是毫秒// 记录旧的数据,下次循环的时候需要用的oldTime = new Date().getTime();oldLoaded = event.loaded;console.log("上传中:>>", event);
};
进度:已经上传的数据
loaded与总数据total的比值
p r o g r e s s = 已上传数据包大小 b y t e 总文件大小 b y t e ∗ 100 % progress= \frac{已上传数据包大小 byte}{总文件大小 byte} { * } {100}{\%} progress=总文件大小byte已上传数据包大小byte∗100%
完善:添加进度条以及速度标识
整体代码如下:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><input type="file" name="file" id="file" /><div>进度: <progress value="0" max="100" id="progress"></progress></div><div>速度: <span id="speed"></span> <span id="unit"></span></div><button id="btn">上传</button><button id="cancelBtn">取消上传</button></body><script>const btn = document.getElementById('btn');let xhr = new XMLHttpRequest();let oldDataSize;let oldTime;btn.onclick = function () {let file = document.querySelector('#file').files[0];console.log(file);// 组装好 formData// 文件传输是通过正文传输的,所以要用 postlet formData = new FormData(); // 这里的 new formData() 会自动帮我设置 content-typeformData.append('data', file);formData.append('name', '文件');formData.append('年龄', 20);// 组装好 xhrxhr.open('post', '/upload');xhr.onload = function () {console.log(xhr.responseText);};xhr.upload.onloadstart = (event) => {console.log('开始上传');oldLoaded = 0;oldTime = new Date();};// onprogress 钩子函数会不停地被调用xhr.upload.onprogress = (event) => {console.log('正在上传:>>', event);// 计算速度let duringLoaded = (event.loaded - oldLoaded) / 1024;let duringTime = (new Date() - oldTime) / 1000; // 时间戳,默认单位是毫秒// 记录旧的数据,下次循环的时候需要用的oldTime = new Date();oldLoaded = event.loaded;let speed = duringLoaded / duringTime; // 单位是 bt/slet unit = 'b/s';if (speed > 1024) {speed = speed / 1024;unit = 'kb/s';}if (speed > 1024) {speed = speed / 1024;unit = 'mb/s';}if (speed > 1024) {speed = speed / 1024;unit = 'gb/s';}if (speed > 1024) {speed = speed / 1024;unit = 'tb/s';}document.getElementById('speed').innerHTML = `${speed}`;document.getElementById('unit').innerHTML = `${unit}`;// 计算进度const { total, loaded } = event;let progress = ((loaded / total) * 100).toFixed(0);document.getElementById('progress').value = progress;};xhr.upload.onload = () => {console.log('上传成功');};xhr.upload.onloadend = () => {console.log('上传完成');};xhr.upload.onabort = () => {console.log('取消上传');};xhr.send(formData);};document.getElementById('cancelBtn').addEventListener('click', function () {// 取消上传xhr.abort();});</script>
</html>
后端
后端获取相应数据的方式如下:
router.post('/upload', ctx => {console.log(ctx.request.body); // 接收文字console.log(ctx.request.files); // 接收文件信息
})
node 会帮我们把二进制文件存储到临时地址,我们可以通过 fs 模块拿到文件,然后写到自己想要的位置

基本功能:拿到二进制数据并转存文件
后端接收注意要在 KoaBody 这里允许上传文件,具体的知识点可以阅读一下这篇博文:理解 HTTP 中的 multipart/form-data
app.use(KoaBody({multipart: true
}))

后端代码如下:
const Koa = require('koa');
const View = require('koa-views');
const Router = require('koa-router');
const { koaBody } = require('koa-body');
const Static = require('koa-static');
const fs = require('fs');
const app = new Koa();
const router = new Router();
app.use(View(__dirname));
app.use(Static(__dirname));
app.use(koaBody({ multipart: true }));
// 异步函数
router.get('/', async (ctx, next) => {await ctx.render('index.html');
});// 异步函数
router.post('/upload', async (ctx, next) => {console.log('ctx.request.files:>>', ctx.request.files);console.log('ctx.request.body:>>', ctx.request.body);const filePath = ctx.request.files.data.filepath;const readFile = fs.readFileSync(filePath);fs.writeFileSync('static/' + ctx.request.files.data.originalFilename, readFile);ctx.body = '请求成功';
});app.use(router.routes());
app.listen(3000, () => {console.log('server start:>>', 'http://localhost:3000');
});
优化:文件夹的判断以及错误处理
可以检测文件夹是否存在,如果文件夹不存在的话自然会报错,完善后的代码如下
/*** 说明:* fs.exists() 已弃用,但 fs.existsSync() 不是。* fs.exists() 的 callback 参数接受与其他 Node.js 回调不一致的参数。 fs.existsSync() 不使用回调* 参考地址:https://nodejs.cn/api/fs/fs_existssync_path.html*/
router.post('/upload', async (ctx, next) => {try {// console.log('ctx.request.files:>>', ctx.request.files);// console.log('ctx.request.body:>>', ctx.request.body);const data = ctx.request.files.data;const { filepath, originalFilename } = data;if (!fs.existsSync(`static`)) {fs.mkdirSync('static');}const readFile = fs.readFileSync(filepath);fs.writeFileSync(`static/${originalFilename}`, readFile);ctx.body = '请求成功';} catch (err) {console.log(err);}
});
断点续传
Q & A
Error: options.maxFileSize (209715200 bytes) exceeded, received 209731427 bytes of file data

这是后端有上传文件大小限制的问题,在 koa-body 配置中把文件改的大一些,默认是 200mb,点我查看源文档

/*** 设置上传文件大小最大限制,默认1000M* https://github.com/node-formidable/formidable*/
app.use(koaBody({multipart: true,formidable: {maxFileSize: 1000 * 1024 * 1024, },})
);
相关文章:
【文件上传系列】No.0 利用 FormData 实现文件上传、监控网路速度和上传进度(原生前端,Koa 后端)
利用 FormData 实现文件上传 基础功能:上传文件 演示如下: 概括流程: 前端:把文件数据获取并 append 到 FormData 对象中后端:通过 ctx.request.files 对象拿到二进制数据,获得 node 暂存的文件路径 前端…...
web前端之JavaScrip的笔试题
MENU Promise笔试题-02prototype和__proto__的笔试题JavaScript引用类型值值操和运算符优先级比较--笔试原型与原型链--笔试-05作用域-笔试事件队列-笔试题JavaScript之变量提升-笔试题JavaScript之原型链--笔试题 Promise笔试题-02 console.log(1); // 宏仁务 2 setTimeout(_…...
生活、工作常用API免费接口
身份证识别OCR:传入身份证照片,识别照片文字信息并返回,包括姓名、身份证号码、性别、民族、出生年月日、地址、签发机关及有效期。二维码识别OCR:对图片中的二维码、条形码进行检测和识别,返回存储的文字内容。银行卡…...
PHP使用mkcert本地开发生成HTTPS证书 PhpEnv集成环境
PHP使用mkcert本地开发生成HTTPS证书 PhpEnv集成环境 前言一、介绍 mkcert二、安装/使用 mkcert1. 安装2. 使用 总结 前言 本地开发时有些功能只有在 https 证书的情况下才能使用, 例如一些 Web API 一、介绍 mkcert Github地址 mkcert 是一个制作本地可信开发证书的简单工具。…...
DHTMLX Scheduler PRO 6.0.5 Crack
功能丰富的 JavaScript调度程序 DHTMLX Scheduler 是一个 JavaScript 日程安排日历,具有 10 个视图和可定制的界面,用于开发任何类型的日程安排应用程序。 DHTMLX JS 调度程序库的主要特性 我们的 JS 调度程序最需要的功能之一是时间轴视图。借助时间轴…...
AddressSanitizer和LeakSanitizer有什么区别
AddressSanitizer(ASan)和LeakSanitizer(LSan)都是用于内存错误检测的工具,它们的主要区别在于检测的问题类型和应用场景。 AddressSanitizer(ASan): ASan是一种用于检测内存错误的工具…...
CoreDNS实战(二)-CoreDNS+ETCD实现DNS服务发现
1 引言 在前面的文章中讲了如何搭建一个内网的DNS服务,但是这里有个问题,mxsm-register.local表示局域网的注册中心域名,如果需要将每台部署了注册中心的IP地址写到CoreDNS的配置文件中。这样就需要每次都去修改 Corefile 配置文件。那有没有…...
B站缓存视频M4S合并MP4(js + ffmpeg )
文章目录 B站缓存视频转MP4(js ffmpeg )1、说明 2、ffmpeg2.1 下载地址2.2 配置环境变量2.3 测试2.4 转换MP4命令 3、处理程序 B站缓存视频转MP4(js ffmpeg ) 注意:这样的方式只用于个人之间不同设备的离线观看。请…...
学习IO的第四天
作业 : 使用两个子进程完成两个文件的拷贝,子进程1拷贝前一半内容,子进程2拷贝后一般内容,父进程用于回收两个子进程的资源 #include <head.h>int main(int argc, const char *argv[]) {int rd -1;if((rdopen("./01_test.c&quo…...
Linux:缓冲区的概念理解
文章目录 缓冲区什么是缓冲区?缓冲区的意义是什么?缓冲区的刷新方式 理解缓冲区用户缓冲区和内核缓冲区缓冲区在哪里? 本篇主要总结的是关于缓冲区的概念理解,以及再次基础上对文件的常用接口进行一定程度的封装 缓冲区 什么是缓…...
中山大学李华山、王彪课题组开发 SEN 机器学习模型,高精度预测材料性能
内容一览:了解全局晶体对称性并分析等变信息,对于预测材料性能至关重要,但现有的、基于卷积网络的算法尚且无法完全实现这些需求。针对于此,中山大学的李华山、王彪课题组,开发了一款名为 SEN 的机器学习模型ÿ…...
二百一十、Hive——Flume采集的JSON数据文件写入Hive的ODS层表后字段的数据残缺
一、目的 在用Flume把Kafka的数据采集写入Hive的ODS层表的HDFS文件路径后,发现HDFS文件中没问题,但是ODS层表中字段的数据却有问题,字段中的JSON数据不全 二、Hive处理JSON数据方式 (一)将Flume采集Kafka的JSON数据…...
【开放集检测OSR】开放集检测和闭集检测的区别和联系:从模型角度进行理解
定义一个分类器: D t r a i n { ( x i , y i ) } i 1 N ⊂ X C D_{train} \{(x_i, y_i)\}^N _{i1} ⊂ X C Dtrain{(xi,yi)}i1N⊂XC X:输入空间 ( x i , y i ) (x_i, y_i) (xi,yi): 输入的图像x以及其对象的类别标签yC :已知…...
【Flink】Flink核心概念简述
目录 一、Flink 简介二、Flink 组件栈1. API & Libraries 层2. runtime层3. 物理部署层 三、Flink 集群架构四、Flink基本编程模型五、Flink 的部署模式六、Flink 任务的执行模式五、Flink 的优点 一、Flink 简介 Apache Flink 的前身是柏林理工大学一个研究性项目&#x…...
linux-tar命令、解压、压缩
压缩 文件夹 命令:tar -zcvf ~/test/tar_t.tar.gz /target/ 将/target/文件夹及其子文件夹和文件压缩成tar_t.tar.gz文件,并放于~/test/路径下 文件 命令:tar -zcvf ~/test/tar_t.tar.gz /target/file 将/target/file文件压缩成tar_t.tar…...
Kafka Connect :构建强大分布式数据集成方案
Kafka Connect 是 Apache Kafka 生态系统中的关键组件,专为构建可靠、高效的分布式数据集成解决方案而设计。本文将深入探讨 Kafka Connect 的核心架构、使用方法以及如何通过丰富的示例代码解决实际的数据集成挑战。 Kafka Connect 的核心架构 Kafka Connect 的核…...
基于 Flink CDC 构建 MySQL 的 Streaming ETL to MySQL
简介 CDC 的全称是 Change Data Capture ,在广义的概念上,只要是能捕获数据变更的技术,我们都可以称之为 CDC 。目前通常描述的 CDC 技术主要面向数据库的变更,是一种用于捕获数据库中数据变更的技术。CDC 技术的应用场景非常广泛…...
创建vue项目:node.js下载安装、配置环境变量,下载安装cnpm,配置npm的目录、镜像,安装vue、搭建vue项目开发环境(保姆级教程一)
今天讲解 Windows 如何创建 vue 项目,搭建 vue 开发环境,这是这个系列的第一章,有什么问题请留言,请点赞收藏!!! 文章目录 一、Vue简单介绍二、开始搭建1、安装node.js环境2、配置npm下载时的默…...
uni-app 微信小程序之好看的ui登录页面(一)
文章目录 1. 页面效果2. 页面样式代码 更多登录ui页面 uni-app 微信小程序之好看的ui登录页面(一) uni-app 微信小程序之好看的ui登录页面(二) uni-app 微信小程序之好看的ui登录页面(三) uni-app 微信小程…...
[ES]ElasticSearch强转日期的时区问题
问题 由于ES不能修改时区,且默认时区始终为UTC。 当才查询数据时,通过强转获得的日期并不是想要的,通过分析发现,正是由于默认时区导致结果相差了8个小时。 查询语句: POST http://localhost:9200/_sql?formattext {&…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...
