粥里有勺糖

vuePress-theme-reco 粥里有勺糖    2018 - 2023
粥里有勺糖 粥里有勺糖

Choose mode

  • dark
  • auto
  • light
关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
author-avatar

粥里有勺糖

285

文章

40

标签

关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
  • devlearn

    • 开发教程
    • 实践:利用ArrayBuffer实现预览指定目录下的所有文件的内容
    • 在linux-deepin上使用deepin-wine5完美运行腾讯会议/QQ/微信等此类应用
    • eslint插件开发教程
    • ServerLess之云函数实践-天气API
    • 移动端阻止弹窗下层页面被滑动
    • 小技巧:for of中获取index
    • Git常用的一些基本操作
    • 向页面注入js实现为图片和文字元素添加透明蒙层
    • 实践:使用jsencrypt配合axios实现非对称加密传输的数据
    • 封装dotenv库实现类似Vite加载环境变量的行为
    • 30行代码实现合并指定目录下的所有文件的内容
    • 马上中秋了,把鼠标指针变为小玉兔
    • Node中require与fs.readFile读取JSON文件的对比
    • 使用免费的七牛云OSS(10G)搭建个人的在线图床
    • 分享封装的一些七牛云OSS操作方法
    • 本地配置SSH免密远程登录服务器
    • 工具方法汇总
    • 腾讯云Serverless实践-Node.js服务部署
    • 腾讯云Serverless实践-静态网站托管
    • 为什么'\x1B'.length === 1?\x与\u知识延伸
    • Vite插件开发纪实:vite-plugin-monitor(上)
    • Vite插件开发纪实:vite-plugin-monitor(中)
    • Vite插件开发纪实:vite-plugin-monitor(下)
    • 解决Vite-React项目中js使用jsx语法报错的问题
    • webpack 项目接入Vite的通用方案介绍
    • webpack 项目接入Vite的通用方案介绍-草稿
    • 优雅的处理挂载window上的函数可能不存在的情况
    • Mac上抓包秒通关羊了个羊

实践:利用ArrayBuffer实现预览指定目录下的所有文件的内容

vuePress-theme-reco 粥里有勺糖    2018 - 2023

实践:利用ArrayBuffer实现预览指定目录下的所有文件的内容

粥里有勺糖 2021-06-09 技术笔记技术教程

# 实践:利用ArrayBuffer实现预览指定目录下的所有文件的内容

# 效果

先上效果图,非常朴素,没有用代码高亮插件,无样式表

图片

  • 完整实例源码 (opens new window)

# ArrayBuffer是什么

ArrayBuffer对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。

货比较硬,这里就不展开详细介绍,后续还是通过实例的方式介绍一下这些硬货的用处

  • 详细了解: 推荐阅读ECMAScript 6 入门 - ArrayBuffer 对象 (opens new window)

# 读取文件内容

  1. 通过使用 FileReader.readAsArrayBuffer方法将File对象转为ArrayBuffer
  2. 使用TextDecoder将内容转为utf8格式的文本
function readFile2Text(file) {
    const fileReader = new FileReader()
    fileReader.readAsArrayBuffer(file)
    return new Promise(resolve=>{
        fileReader.onload = function () {
            const buf = this.result
            const textDecoder = new TextDecoder('utf8')
            resolve(textDecoder.decode(buf))
        }
    })
}
1
2
3
4
5
6
7
8
9
10
11

当然这里也可直接用FileReader.readAsText

为了契合主题强行readAsArrayBuffer

function readFile2Text(file) {
    const fileReader = new FileReader()
    fileReader.readAsText(file)
    return new Promise(resolve=>{
        fileReader.onload = function () {
            resolve(this.result)
        }
    })
}
1
2
3
4
5
6
7
8
9

# 读取指定目录下的所有文件

使用input标签选择目录,只需要给input标签添加webkitdirectory与multiple属性即可,详细介绍请查阅MDN:<input type="file"> (opens new window):

  • webkitdirectory: 只允许选择目录
  • multople: 允许选择多个文件

页面代码

<input type="file" id="file" webkitdirectory multiple>
1

监听dom的onchange事件,获取选择的所有文件(不包含空目录)

逻辑

const $file = document.getElementById('file')

// 选择目录
$file.onchange = function () {
    const files = this.files
    // 打印获取所有的文件
    console.log(files)
}
1
2
3
4
5
6
7
8

每个file对象包含以下属性

{
    name: String, // 文件吗
    size: Number, // 文件大小
    type: String, // 文件MIME类型
    webkitRelativePath: String, // 文件相对路径
}
1
2
3
4
5
6

# 树型目录生成

使用ul配合li实现

# Dom结构

<div id="lists" style="width: 36%;">
    <ul>
        <li path="test" deep="0">test
            <ul>
                <li path="test/logo.jpeg" deep="1">logo.jpeg</li>
                <li path="test/1" deep="1">1
                    <ul>
                        <li path="test/1/1-2" deep="2">1-2
                            <ul>
                                <li path="test/1/1-2/index.js" deep="3">index.js</li>
                            </ul>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

展示

  • test
    • logo.jpeg
    • 1
      • 1-2
        • index.js

# 具体实现

页面代码

<input type="file" id="file" webkitdirectory multiple>
<div id="lists"></div>
1
2

逻辑

将文件的相对路径通过/拆分:test/abc/index.js => ['test','abc','index.js']

const $file = document.getElementById('file')
const $lists = document.getElementById('lists')
// 选择目录
$file.onchange = function () {
    const files = this.files
    // 全部清空
    $lists.innerHTML = ''
    // 拆解目录
    for (const f of files) {
        f.paths = f.webkitRelativePath.split('/')
    }
    appendDir($lists, files)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

appendDir:

  • parent:父节点
  • files: 文件
  • deep: 目录深度

通过此方法,生成指定某一级的目录

function appendDir(parent, files, deep = 0) {
    const $ul = document.createElement('ul')
    // 使用Set存储所有的文件的公共前缀
    // 利用Set自动去重
    const dirs = new Set()
    for (const f of files) {
        // 取相同深度的目录
        const p = f.paths[deep]

        // 深度为0,说明是选择的那一个目录
        if (deep === 0) {
            dirs.add(p)
        } else {
            // 获取父节点对应的相对目录
            const parentP = parent.getAttribute('path')
            // 判断文件是否属于此父目录
            if (f.webkitRelativePath.startsWith(parentP)) {
                // 存放符合条件的文件路径
                dirs.add([parentP, p].join('/'))
            }
        }
    }
    // 
    for (const d of dirs) {
        const $li = document.createElement('li')

        // 只展示文件名/或目录名 (test/abc/index.js => index.js)
        const idx = d.lastIndexOf('/') + 1
        $li.textContent = idx===0 ? d : d.slice(idx)

        // 记录这个节点的深度与完整相对路径
        $li.setAttribute('path', d)
        $li.setAttribute('deep', deep)
        $ul.appendChild($li)
    }
    // 插入页面
    if (dirs.size !== 0) {
        parent.appendChild($ul)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

利用事件代理监听li的点击事件

$lists.addEventListener('click', function (e) {
    const $li = e.target
    // 不是li不管
    if ($li.tagName.toLowerCase() !== 'li') {
        return
    }
    // 获取点击节点的路径与深度
    const path = $li.getAttribute('path')
    const deep = +$li.getAttribute('deep')

    // 获取选择的所有文件
    const files = $file.files

    // 遍历文件,判断点击的是文件还是目录

    for (const f of files) {
        // 点击的文件
        if (f.webkitRelativePath === path) {
            // 预览内容
            previewFile(f)
            return
        } 
    }

    // 有子项,点击的目录且未被点击添加过
    if ($li.children.length === 0) {
        appendDir($li, files, deep + 1)
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

到此的生成目录效果如下:

图片

# 最后

ArrayBuffer的内容还是比较多,本文只简单讲了利用其获取文件内容

本文主要内容还是实践生成目录的树形结构,由于时间仓促,代码还有很多的优化空间

Edit this page (opens new window)
Last Updated: 2022/5/15 12:46:34