来源 | https://betterprogramming.pub/a-complete-guide-of-file-uploading-in-javascript-2c29c61336f5

翻译 | 杨小爱

文件上传是 Web 项目的常用功能。相信大家在开发过程中或多或少都遇到过相关的需求。

在今天的这篇文章中,我总结了一些场景和解决方案,希望能帮助你彻底掌握文件上传相关的问题。

我们的目标

首先,我们来明确一下文件上传的具体功能。

根据上传目标,有3种任务:

  • 上传单个文件

  • 同时上传多个文件

  • 上传整个文件夹

根据用户操作,有:

  • 选择要上传的文件

  • 将文件拖到框中然后上传

  • 从剪贴板上传

从性能的角度来看,我们可能需要:

  • 压缩文件后上传

  • 将大文件分成块然后上传

另外,有时我们可能不会在客户端浏览器上传文件,而是通过服务器上传到另一台服务器。

我们将依次讨论这些。

准备工作

在开始编程工作之前,我们还是需要了解一些背景知识。

  • 首先,在上传文件时,我们使用最流行的 HTTP 库 Axios。在实际开发中,我们一般不直接使用 XMLHttpRequest,使用 Axios 符合真实的开发模式。

  • 当我们在前端讨论上传文件的时候,要想全面了解相关原理,就必须了解相关的后端代码。这里我们使用 Koa 来实现我们的服务器。

  • 最后希望大家对formdata有一个简单的了解,我们使用这种数据格式来上传文件。

上传单个文件

传单个文件的需要太常见了。例如,当我们注册微信后,我们可能需要上传更改一个头像。

文件上传功能需要客户端和服务器的配合。在我们的项目中,用户在客户端选择一个文件,然后上传到服务器;服务器保存文件并返回它的 URL。

这是项目预览:

上面的 Gif 展示了文件上传的完整过程:

  • 用户在浏览器中选择文件

  • 用户点击上传按钮

  • 上传的文件放在服务器的uploadFiles文件夹中

  • 然后服务器返回一个URL,就是上传文件的地址

  • 用户可以通过这个 URL 访问资源

编码

这个项目的所有代码都保存在 GitHub 上:

你可以将其克隆到你的计算机:

# clone it
$ git clone git@github.com:BytefishMedium/FileUploading.git
# and install npm package
$ cd FileUloading
$ npm install

所有与单个文件上传相关的代码都放在 1-SingleFile 文件夹中。

  • client.html 与客户端代码相关。

  • server.js 与服务器端代码相关

要运行服务器,你可以转到该文件夹并运行以下命令:

$ node server.js

然后,我们可以在任何浏览器上打开 client.html。

具体操作我已经在上面的gif中展示过了。你可以先自己尝试一下,然后继续阅读。

客户端代码

嗯,把大象放进冰箱需要多少步骤?

只需三步:

  1. 打开冰箱门

  2. 把大象放进去

  3. 关上门。

上传文件也是如此,我们只需要三个步骤:

  1. 让用户选择要上传的文件

  2. 阅读此文件

  3. 使用axios上传文件

在 HTML 中,我们可以使用 input 元素。只需将此元素的类型设置为文件,然后该元素即可用于选择文件。

<input id="fileInput" type="file"/>

用户选择文件后,该文件的元数据将存储在此输入元素的 files 属性中。

const uploadFileEle = document.getElementById("fileInput")
console.log(uploadFileEle.files[0]);

最后,我们使用 Axios 的 post 方法来上传文件。但是在上传文件之前,我们还需要把这个文件打包成FormData格式。

let file = fileElement.files[0];
let formData = new FormData();
formData.set('file', file);
axios.post("http://localhost:3001/upload-single-file", formData).then(res => {console.log(res)
})

提示:FormData 是一种键值类型的数据格式。这是一个例子:

好了,这些就是文件上传相关的知识点了,更完整的代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="  https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script>
</head><body><input type="file" id="fileInput"><button id="uploadButton">upload</button><script>document.getElementById("uploadButton").onclick = () => {let fileElement = document.getElementById('fileInput')// check if user had selected a fileif (fileElement.files.length === 0) {alert('please choose a file')return}let file = fileElement.files[0]let formData = new FormData();formData.set('file', file);axios.post("http://localhost:3001/upload-single-file", formData, {onUploadProgress: progressEvent => {const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);console.log(`upload process: ${percentCompleted}%`);}}).then(res => {console.log(res.data)console.log(res.data.url)})}
</script>
</body></html>

这段代码其实就是为了实现我们之前说的三个步骤:

只是我们增加了两个额外的功能:

  • 一是上传按钮。当用户点击上传按钮时,我们开始执行上传逻辑。

  • 然后我们再做一个判断,确保用户真的选择了一个文件。

然后,当axios上传文件时,它可以让我们监控文件上传的进度。

我们知道 HTTP 是建立在 TCP 之上的。如果一个HTTP报文比较大,可能会分解成多个不同的TCP报文在网络中传输。

如果你需要写一个进度条来向用户展示上传的进度,你可以使用这个 API。

axios.post("http://localhost:3001/upload-single-file", formData, {onUploadProgress: (progressEvent) => {const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);console.log(`upload process: ${percentCompleted}%`);},
});

progressEvent.loaded 表示上传成功的字节数,progressEvent.total 表示文件的总字节数。

好的,这是我们的客户端代码。

服务器端代码

要启动服务器,我们可以使用 Koa。这是一个使用 Koa 的小型服务器:

const path = require("path");
const Koa = require("koa");
const Router = require("@koa/router");const app = new Koa();
const router = new Router();const PORT = 3000;router.get("/", async (ctx) => {ctx.body = "Hello friends!";
});app.use(router.routes()).use(router.allowedMethods());app.listen(PORT, () => {console.log(`app starting at port ${PORT}`);
});
view rawkoa.server.v1.js hosted with ❤ by GitHub

这是最基本的 Koa 演示。由于本文重点介绍文件上传,所以我就不详细解释了。如果你不熟悉这个,你可以阅读官方文档。

  • Koa:https://github.com/koajs/koa

  • Koa-router:https://github.com/koajs/router

我们的客户端使用FormData的格式上传文件,那么,我们的服务器也需要解析FormData。而 Koa-multer 是一个帮助我们解析 FormData 数据的中间件:

const path = require("path");
const Koa = require("koa");
const Router = require("@koa/router");
const multer = require("@koa/multer");
const cors = require("@koa/cors");const app = new Koa();
const router = new Router();const PORT = 3000;const upload = multer();router.get("/", async (ctx) => {ctx.body = "Hello friends!";
});// add a route for uploading single files
router.post("/upload-single-file", upload.single("file"), (ctx) => {console.log("ctx.request.file", ctx.request.file);ctx.body = `file ${ctx.request.file.filename} has saved on the server`;
});app.use(cors());
app.use(router.routes()).use(router.allowedMethods());app.listen(PORT, () => {console.log(`app starting at port ${PORT}`);
});

关于 Koa-multer,你可以阅读他们的官方文档:

  • Koa-multer:https://github.com/koajs/multer

  • Multer:https://github.com/expressjs/multer

关键代码是uoload.single('file'),这行代码可以帮助我们解析FormData的数据,然后,把对应的信息放到ctx.request.file中。

其实,此时我们的服务器已经可以接收到客户端上传的文件了,但是,接收到文件后并没有存储到磁盘中。

如果我们想让 Koa-multer 为我们将文件保存到磁盘,我们可以添加以下配置:

const storage = multer.diskStorage({destination: function (req, file, cb) {cb(null, UPLOAD_DIR);},filename: function (req, file, cb) {cb(null, `${file.originalname}`);},
});
const upload = multer({ storage: storage });

完整的代码是 server.js,你可以直接在代码仓库中阅读。

当前的流程图如下所示:

无论如何,我们应该自己尝试一下。

上传多个文件

有了上面的基础,我们编写上传多个文件的代码就简单多了。

首先,我们需要修改 input 元素并为其添加 multiple 属性。

<input type="file" id="fileInput" multiple>

这是告诉浏览器现在这个输入元素应该允许用户同时选择多个文件。

然后用户选择多个文件后,数据会放在fileElement.files中。我们在构造formdata的时候,需要遍历这个列表,把所有的文件都放到formdata中。

let files = Array.from(fileElement.files);
let formData = new FormData();
files.forEach((file) => {formData.append("file", file);
});

那么上传文件的代码就不需要修改了。

这是完整的代码:

该文件位于项目中的 2-MultipleFiles/client.html 上。

同时,我们还需要调整服务端的代码。

首先,我们需要添加对应的路由/upload-multiple-files

, 然后使用 upload.fields([{ name: “file” }]) 中间件来处理多个文件。之后request中的FormData数据会被放到ctx.files.file中。

演示:

上传目录

现在让我们看一下上传目录的步骤。

与之前类似,我们需要将 input 元素的属性设置为:

<input type="file" id="fileInput" webkitdirectory>

那么在上传目录的时候,input元素的files对象会有webkitRlativePath属性,我们也将它们添加到formdata中。

这里需要注意的是,当文件名中包含\时,koa-multer可能会报错。要解决此问题,我们需要将 \ 替换为 @ 符号。

formData.append('file', file, file.webkitRelativePath.replace(/\//g, "@"));

那么我们还需要修改对应的服务器代码:

演示:

总结

我们依次分析了上传单个文件、多个文件、目录的过程。其实很简单,只要3步:

  • 使用输入元素让用户选择文件

  • 读取文件并构造FormData

  • 使用 Axios 上传 FormData

所有代码都在 GitHub 上,大家可以自己试试。如果您有任何问题,可以发表评论。

由于文章篇幅关系,剩下的文件上传会在后面的文章中介绍,如果您对此内容感兴趣,可以关注我,感谢你的阅读。

PS:

免费送书活动的中奖名单如下:

@Echo、@Mint、@知己、@璐、@Mr.simple、@咩咩咩 。

另外,因为合作出版社根据我们这边活动的阅读情况,又额外增加了2个名额,所以,还是一样按照规则顺序下来,这两位惊喜幸运者是,

@

JavaScript 文件上传完整指南,附【图书中奖者名单】相关推荐

  1. php视频上传教程,PHP实现视频文件上传完整实例,_PHP教程

    PHP实现视频文件上传完整实例, 本文以一个完整实例的形式实现了视频文件上传的功能.虽然是比较基础的应用,仍有一定的参考价值.分享给大家供大家参考之用.具体方法如下: 首先,对PHP来说视频也属于文件 ...

  2. Github 之 本地上传代码到 Github ,并且添加 .gitignore 文件 屏蔽一些文件上传(内附详细步骤)

    Github 之 本地上传代码到 github ,并且添加 .gitignore 文件 屏蔽一些文件上传(内附详细步骤) 目录 Github 之 本地上传代码到 github ,并且添加 .gitig ...

  3. SWFUpload多文件上传使用指南

    SWFUpload是一个flash和js相结合而成的文件上传插件,其功能非常强大.以前在项目中用过几次,但它的配置参数太多了,用过后就忘记怎么用了,到以后要用时又得到官网上看它的文档,真是太烦了.所以 ...

  4. Struts2 + uploadify 多文件上传完整的例子!

    首先,我这里使用的是  Jquery  Uploadify3.2版本号  导入相关的CSS  JS    <link rel="stylesheet" type=" ...

  5. struts2+extjs文件上传完整实现(攻克了上传中的各种问题)

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/shanhuhau/article/details/28617999 首先须要引入上传控件 <s ...

  6. javascript --- 文件上传即时预览 闭包实现多图片即时预览

    使用javascript原生功能实现,点击上传文件,然后再网页上显示出来 1. 初级显示 1.1 准备一个input标签和一个img标签 <input type=file id="fi ...

  7. JavaScript 文件上传详解

    本文为 Qunar 技术沙龙投稿,版权归原作者所有,未经允许,请勿转载. 原文地址:http://mp.weixin.qq.com/s/KWFyJa06CNXrU8zhSzzQFQ 作者:梁志,201 ...

  8. php上传视频文件代码,PHP视频文件上传完整示例代码

    这里有新鲜揭晓的PHP面向对象编程,程序猫速率看过来! PHP开源脚本语言PHP(外文名: Hypertext Preprocessor,中文名:"超文本预处理器")是一种通用开源 ...

  9. 微信小程序云开发图片上传完整代码附效果图

    在app.json里面加如下代码, 使用 WeUI组件库.点击跳转 "useExtendedLib": {"weui": true}, 先看效果图 wxml & ...

  10. php.ini 米拓_MetInfo(米拓) v5.1.3任意文件上传漏洞分析附利用EXP | CN-SEC 中文网

    摘要 MetInfo 23号发布了新版本5.1.5,修补了本文提到的漏洞,当然严格来说应该是任意变量覆盖漏洞-. ps:欢迎各种形式转载,首发t00ls.net MetInfo 23号发布了新版本5. ...

最新文章

  1. java 判断网络类型_Android 网络类型判断(2g、3g、wifi)及IP地址获取
  2. 用STL给C++充电:第一部分
  3. Anaconda conda常用命令
  4. php curl_multi_close,PHP curl_multi_close函数
  5. EM算法在二维高斯混合模型参数估计中的应用
  6. 一批工业机械网站交换友情链接
  7. 日常软件使用系列收集
  8. 联想软件商店安装教程
  9. Power Management of Hybrid DRAM/PRAM-Based Main Memory
  10. 计算机远程操作之后怎么保护,电脑远程控制怎么操作 两种方法介绍
  11. 2020年十七届华为杯数学建模比赛记录
  12. 同一个无线局域网(wifi)内,两台电脑无法通过ip通信
  13. 自己写的一点福利代码(一)
  14. js-for循环-9*9乘法表小练习
  15. 【软件建模与UML】(持续更新)
  16. chrome审查元素功能
  17. lower_bound,upper_bound的第四个参数
  18. nolo手柄配对不上_NOLO手柄助手下载
  19. 哈工大软件过程与工具复习1——第1-2讲 概论与核心思想
  20. 1027: 判断水仙花数 Java

热门文章

  1. Supervisor 使用说明,轻松管理进程
  2. oracle 如何实现excel的正态分布函数normdist
  3. python 获取macd数据_60分钟MACD数据如何获取
  4. 关于Cfree5.0编译的常见问题和使用教程
  5. 线性约束最优化问题的Frank-Wolfe方法
  6. python链家数据分析_利用Python分析北京链家二手房数据
  7. 鉴源实验室丨汽车网络安全需求分析方法综述
  8. CAD中怎么在线缆上输入或删除文字?
  9. 预装WIN8的电脑是GPT分区模式,无法安装WIN7
  10. iOS开发中使用代码控制横竖屏的切换