文中转载微信公众平台「全栈开发修仙之路」,创作者阿歪歪鱼。转截文中请联络全栈开发修仙之路微信公众号。
在日常工作上,上传文件是一个很普遍的作用。在文件上传时,我们可以挑选提交单独文档,还可以根据设定 multiple 特性来提交好几个文档。
文中阿歪歪鱼将详细介绍怎样提交文件目录及怎样缩小文件目录并提交,缩小文件目录的作用是根据 JSZip 这一库来完成。运用这一库还能够完成线上浏览 ZIP 文档的作用,有兴趣的小伙伴们能够阅读文章 JavaScript 怎样在线解压 ZIP 文档? 本文。下边大家先来详细介绍怎样完成缩小文件目录并提交的作用。
1.1 挑选文件目录
在电脑浏览器端,要完成缩小文件目录并提交的作用。最先我们要先完成挑选文件目录的作用,要完成该作用,我们可以立即应用 HTMLInputElement 原素的 webkitdirectory 特性:
- <input type="file" id="uploadFile" webkitdirectory />
当设定了 webkitdirectory 特性以后,大家就可以挑选文件目录了。当阿歪歪鱼挑选了 useAxios 文件目录以后,便会表明下列确定框:
点一下提交按键以后,大家就可以获得文档目录。目录中的文档目标上带有一个 webkitRelativePath 特性,用以表明当今文档的绝对路径。在开展文件目录缩小的情况下,大家便会应用到该特性。
尽管根据 webkitdirectory 特性能够非常容易地完成挑选文件目录的作用,但在具体新项目中大家还必须考虑到它的兼容模式。例如在 IE 11 下列的版本号就不兼容该特性,其他电脑浏览器的兼容模式如下图所显示:
(图片出处 —— https://caniuse.com/?search=webkitdirectory)
1.2 缩小文件目录
在 JavaScript 怎样在线解压 ZIP 文档? 本文中,阿歪歪鱼详细介绍了在电脑浏览器端怎么使用 JSZip 这一库完成在线解压 ZIP 文档的作用。JSZip 这一库除开能够分析 ZIP 文档以外,它还能够用于建立和编写 ZIP 文档。这儿阿歪歪鱼根据 JSZip 库出示的 API,封裝了一个 generateZipFile 涵数:
- function generateZipFile(
- zipName, files,
- options = { type: "blob", compression: "DEFLATE" }
- ) {
- return new Promise((resolve, reject) => {
- const zip = new JSZip();
- for (let i = 0; i < files.length; i ) { // 加上文件目录中包括的文档
- zip.file(files[i].webkitRelativePath, files[i]);
- }
- zip.generateAsync(options).then(function (blob) { // 转化成zip文件
- zipName = zipName || Date.now() ".zip";
- const zipFile = new File([blob], zipName, {
- type: "application/zip",
- });
- resolve(zipFile);
- });
- });
- }
在之上编码中,大家应用 file(name, data [,options]) 方式,把文件目录中的文档先后加上到 zip 目标中,随后再根据 generateAsync 方式来转化成 ZIP 文档。在转化成 ZIP 文档时,我们可以设定该文件的种类。这儿大家设定的默认设置种类为 blob 种类,除开适用 blob 种类以外,它还适用 base64、uint8array 和 arraybuffer 等种类。
1.3 提交缩小 ZIP 文档
在缩小目录生成 ZIP 文档以后,大家就可以根据 XMLHttpRequest 或 fetch API 来提交压缩包。下边阿歪歪鱼将以 axios 为例子,来完成上传文件的作用。
html 编码
- <input type="file" id="uploadFile" webkitdirectory />
- <button id="submit" onclick="uploadFile()">文件上传</button>
js 编码
- const uploadFileEle = document.querySelector("#uploadFile");
- const uploadOptions = { needZip = true };
- const request = axios.create({
- baseURL: "http://localhost:3000/",
- timeout: 5000,
- });
- async function uploadFile({ needZip } = uploadOptions) {
- if (!uploadFileEle.files.length) return;
- let fileList = uploadFileEle.files;
- if (needZip) { // 对文件目录开展ZIP缩小
- let webkitRelativePath = fileList[0].webkitRelativePath;
- let zipFileName = webkitRelativePath.split("/")[0] ".zip";
- fileList = [await generateZipFile(zipFileName, fileList)];
- }
- uploadFiles({ // 文件上传目录
- url: "/upload/multiple",
- files: fileList,
- });
- }
在 uploadFile 涵数中,如果有开启文件目录缩小作用,大家便会启用 generateZipFile 涵数转化成 ZIP 文档,要是没有得话,便会立即启用 uploadFiles 涵数来提交文件目录中的全部文档,自然你也能够对文档目录开展过虑,例如限定文件属性或文档的尺寸等。
下边大家看来一下 uploadFiles 涵数的实际完成:
- function uploadFiles({ url, files, fieldName = "file" }) {
- if (!url || !files.length) return;
- let formData = new FormData();
- for (let i = 0; i < files.length; i ) {
- formData.append(fieldName, files[i], files[i].name);
- }
- return request.post(url, formData);
- }
在 uploadFiles 涵数中,大家根据建立 FormData 目标来储存文档的信息内容,随后根据 request(axios 案例)来实行提交实际操作。
2.1 接受 ZIP 文档
在服务器端要完成上传文件作用也非常简单,这儿阿歪歪鱼以 koa 为例子来完成上传文件的作用。假如你对 koa 还不掌握得话,提议你先大概访问 一下 koa 的官方网文本文档。
- const path = require("path");
- const Koa = require("koa");
- const cors = require("@koa/cors");
- const multer = require("@koa/multer");
- const Router = require("@koa/router");
- const app = new Koa();
- const router = new Router();
- const UPLOAD_DIR = path.join(__dirname, "/public/upload");
- const storage = multer.diskStorage({
- destination: async function (req, file, cb) { // 设定文档的储存文件目录
- cb(null, UPLOAD_DIR);
- },
- filename: function (req, file, cb) { // 设定文件夹名称
- cb(null, `${file.originalname}`);
- },
- });
- const multerUpload = multer({ storage });
- router.get("/", async (ctx) => {
- ctx.body = "缩小文件名称提交实例(阿歪歪鱼)";
- });
- router.post(
- "/upload/multiple",
- multerUpload.fields([
- {
- name: "file",
- },
- ]),
- async (ctx, next) => {
- ctx.body = {
- status: "success",
- msg: "上传文件取得成功",
- };
- }
- );
- // 申请注册分布式数据库
- app.use(cors());
- app.use(router.routes()).use(router.allowedMethods());
- app.listen(3000, () => {
- console.log("app starting at port 3000");
- });
在之上编码中,大家根据 @koa/multer 这一分布式数据库来解决上传文件,对该分布式数据库有兴趣的小伙伴们,能够自主阅读文章官方网文本文档。下面,大家来再次探讨另一个难题 —— 怎样接受文件目录并依照文件名称构造开展储放?
2.2 接受文件名称
前边大家早已了解,当 input[type="file"] 应用了 webkitdirectory 特性以后,回到 File 目标的 webkitRelativePath 特性便会储放当今文档相对性于文件列表的绝对路径:
因而在我们在服务器端解决文件名称提交的作用时,大家就可以根据该特性来建立相匹配的文件目录构造,实际的解决逻辑性以下所显示:
- const fse = require("fs-extra");
- const storage = multer.diskStorage({
- destination: async function (req, file, cb) {
- // 把useAxios@demo.vue中的@更换为途径分节符
- let relativePath = file.originalname.replace(/@/g, path.sep);
- let index = relativePath.lastIndexOf(path.sep);
- let fileDir = path.join(UPLOAD_DIR, relativePath.substr(0, index)); // 转化成文件路径
- await fse.ensureDir(fileDir); // 保证 文件列表存有
- cb(null, fileDir);
- },
- filename: function (req, file, cb) {
- let parts = file.originalname.split("@"); // 对途径开展分拆
- cb(null, `${parts[parts.length - 1]}`); // 获得文件夹名称
- },
- });
为何 originalname 文档初始名字会包括 @ 标记呢?那样由于应用 useAxios/demo.vue 这类途径方式时,是不可以获得到详细的途径名字,只有获得到文件夹名称。为了更好地处理这个问题,阿歪歪鱼在文件上传时,手动式把文档绝对路径中的 / 标记更换为 @ 随后再开展提交,相匹配的解决逻辑性以下:
- function uploadFiles({ url, files, fieldName = "file" }) {
- if (!url || !files.length) return;
- let formData = new FormData();
- for (let i = 0; i < files.length; i ) {
- formData.append(fieldName, files[i], files[i].webkitRelativePath.replace(/\//g, "@"));
- }
- return request.post(url, formData);
- }
好的,缩小文件目录提交和文件目录提交早已详细介绍完后,有兴趣的小伙伴们能够动手能力试一试。因为详细的实例编码內容比较多,阿歪歪鱼也不放实际的编码了。有必须的小伙伴们,能够浏览下列详细地址访问 实例编码。
https://gist.github.com/semlinker/af57349c16d203cc2ec845d4b5a6b445
留意:之上编码仅作参考,请依据具体业务流程开展调节。
文中阿歪歪鱼详细介绍了怎样运用 input[type="file"] 原素的 webkitdirectory 特性来完成挑选文件目录的作用,随后运用 JSZip 这一库来完成文件目录缩小,最终根据 axios 来提交文件目录缩小后的 ZIP 文档 。除此之外,阿歪歪鱼还详细介绍了怎么使用 koa 来完成接受文件目录并依照文件名称构造开展储放的作用。
JSZip 官方网文本文档
MDN - webkitdirectory
JavaScript 怎样在线解压 ZIP 文档?