web浏览器使用FileSystem的API进行本地文件的存储

news2024/12/23 23:18:03

作者:程序员CKeen
博客:http://ckeen.cn

长期坚持做有价值的事!积累沉淀,持续成长,升维思考!希望把编码作为长期兴趣爱好😄

目录

      • 1. 背景
      • 2. Web端本地File操作的API
      • 3. File and Directory Entries API的介绍
      • 4. 代码实现


1. 背景

最近遇到一个很特别的需求,需要将嵌入式设备的里面的视频流和图片通过websocket直接传到浏览器端进行显示和存储(因为设备的存储空间较有限,不能将视频和图片存在设备上),并且本地浏览器还可以查看视频和图片列表以及提供视频和图片的本地下载。不过还好的是,不用考虑所有浏览器。具体需求参考如下:

image-20230507091705794

通过分析, 貌似要做的就是将websocket传过来的视频裸流和图片数据,通过web端浏览器接口直接存储在本地,以及从本地读取这些文件。 于是就开始去找web浏览器关于本地文件操作的方法, 在MDN找到了web端对File操作的相关的API。

参考链接:https://developer.mozilla.org/en-US/docs/Web/API

2. Web端本地File操作的API

MDN介绍的File相关的API主要有以下三个:

  • File API 。文件API使web应用程序能够访问文件及其内容。

    当用户使文件可用时,Web应用程序可以访问文件,可以使用file 元素,也可以通过拖放操作。

    以这种方式提供的文件集被表示为FileList对象,它使web应用程序能够检索单个File对象。反过来,File对象提供对元数据的访问,例如文件的名称、大小、类型和最后修改日期。

    文件对象可以传递给FileReader对象来访问文件的内容。FileReader接口是异步的,但只有在web worker中可用的同步版本由FileReaderSync接口提供。

  • File and Directory Entries API。
    文件和目录条目API模拟了一个本地文件系统,web应用程序可以在其中导航和访问文件。你可以开发在虚拟的沙盒文件系统中读取、写入和创建文件和/或目录的应用。

  • File System Access API。
    文件系统访问API允许读、写和文件管理功能。该API允许与用户本地设备上的文件或用户可访问的网络文件系统上的文件进行交互。该API的核心功能包括读取文件、写入或保存文件以及访问目录结构。

    大多数与文件和目录的交互都是通过句柄完成的。父类FileSystemHandle帮助定义两个子类:FileSystemFileHandle和FileSystemDirectoryHandle,分别用于文件和目录。

而今天我们主要用到的是文件和目录的API:File and Directory Entries API。该API提供了对本地目录的创建,以及对文件的创建和读写操作。

3. File and Directory Entries API的介绍

上面已经简单了介绍了File and Directory Entries API的用途,下面我们主要介绍该分类下的API接口。

该API详细的介绍可以参考提案:https://wicg.github.io/entries-api/#api-domfilesystem

文件和目录的API主要有以下5个

  1. FileSystem:表示文件系统。当我们需要对文件进行操作时,需要先获取FileSystem的对象,然后通过FileSystem对象来操作具体的目录和文件对象。
  2. FileSystemEntry:表示文件系统中单个条目的基本接口。这是由表示文件或目录的其他接口实现的。
  3. FileSystemDirectoryEntry:表示文件系统中的单个目录。
  4. FileSystemFileEntry:表示文件系统中的单个文件。
  5. FileSystemDirectoryReader:该接口通过调用 FileSystemDirectoryEntry.createReader() (en-US) 创建,提供了允许读取目录内容的功能。

下面是不同浏览器对File and Directory Entries API支持的情况:

image-20230507094215934

这里没有对浏览器提出要求,我们选择使用chrome浏览器来进行测试

  1. FileSystem
    文件和目录条目API接口文件系统用于表示文件系统。这些对象可以从任何文件系统条目的文件系统属性中获得。一些浏览器提供了额外的api来创建和管理文件系统,比如Chrome的requestFileSystem()方法。后面我将通过Chrome的全局方法获取该对象

    有两种方法可以访问FileSystem对象:

    1. 你可以通过调用window.requestFileSystem()直接请求一个代表为你的web应用创建的沙盒文件系统。如果调用成功,则执行回调处理程序,该处理程序接收描述文件系统的FileSystem对象作为参数。

    2. 您可以通过文件系统条目对象的文件系统属性获得它。
      实例属性
      filessystem .name
      表示文件系统名称的字符串。这个名称在整个公开的文件系统列表中是唯一的。

      filessystem.root
      FileSystemDirectoryEntry对象,表示文件系统的根目录。通过该对象,可以访问文件系统中的所有文件和目录。

  2. FileSystemEntry

    文件和目录条目API的FileSystemEntry接口表示文件系统中的单个条目。条目可以是文件或目录(目录由FileSystemDirectoryEntry接口表示)。它包括处理文件的方法——包括复制、移动、删除和读取文件——以及它指向的文件的信息——包括文件名和从根目录到条目的路径。

    您不直接创建FileSystemEntry对象。相反,您将通过其他api接收基于此接口的对象。该接口作为FileSystemFileEntry和FileSystemDirectoryEntry接口的基类,这两个接口分别提供特定于表示文件和目录的文件系统项的特性。

    FileSystemEntry接口包括操作文件和目录所需的方法,但它还包括获取条目URL的方便方法:toURL()。它还引入了一个新的URL方案:filesystem:。

    你可以在Google Chrome上使用filesystem: scheme来查看存储在应用程序原点的所有文件和文件夹。只需使用filesystem: scheme作为应用程序原点的根目录。例如,如果你的应用程序位于http://www.example.com,在一个选项卡中打开filesystem:http://www.example.com/temporary/。Chrome显示了存储在你的应用程序的所有文件和文件夹的只读列表。

    属性(只读):

    fullPath: 相对于虚拟文件系统的根目录的完整路径
    isDirectory: 是否为目录
    isFile: 是否为文件
    name: entry的名称
    

    实例方法:

    copyTo(): 将当前目录或者文件拷贝到心的位置
    getMetadata(): 获取file的元信息,比如修改时间及大小
    getParent(): 获取父目录
    moveTo(): 将当前文件或者目录移动到新位置,也可以用来重命名文件和目录
    remove(): 移除文件和目录, 如果是移除目录需要保证目录为空
    
  3. FileSystemDirectoryEntry

    文件和目录条目API的FileSystemDirectoryEntry接口表示文件系统中的目录。它提供了一些方法,使访问和操作目录中的文件以及访问目录中的条目成为可能。

    您可以通过调用getDirectory()来创建一个新目录。如果要创建子目录,请依次创建每个子目录。如果尝试使用包含尚不存在的父目录的完整路径创建目录,则返回错误。因此,通过在创建父目录后递归地添加新路径来创建层次结构。

    createReader()
    
    
  4. FileSystemFileEntry

    文件和目录项API的FileSystemFileEntry接口表示文件系统中的一个文件。它提供了描述文件属性的属性,以及file()方法,该方法创建可用于读取文件的file对象。

    实例属性
    继承其父接口FileSystemEntry的属性,但没有此接口独有的属性。

    file(successCallback)
    file(successCallback, errorCallback)
    createWriter(successCallback)
    createWriter(successCallback, errorCallback)
    

    image-20230710181515281

  5. FileSystemDirectoryReader
    文件和目录条目API的FileSystemDirectoryReader接口允许您访问代表目录中每个条目的基于FileSystemFileEntry的对象(通常是FileSystemFileEntry或FileSystemDirectoryEntry)。
    最重要的方法:

    readEntries()
    

    返回一个包含一定数量目录项的数组。数组中的每一项都是一个基于FileSystemEntry的对象——通常是FileSystemFileEntry或FileSystemDirectoryEntry。

4. 代码实现

下面我们主要通过一个demo,来实现使用FileSystem的API将图片存储到本地,从本地文件系统进行获取以及下载

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script>

    // 这里为文件base64数据,实际过程中可以自己生成一个图片base64的数据,或者是从过websocket传输一个base64文件
    var base64ImageData = "xxxxx";

    /**
     * 将base64转为Blob对象
     * @param base64
     * @param contentType
     */
    function base64ToBlob(base64, contentType){
        var arr = base64.split(',')         //去掉base64格式图片的头部
        var bstr = atob(arr[1])             //atob()方法将数据解码
        var leng = bstr.length
        var u8arr = new Uint8Array(leng)
        while(leng--){
            u8arr[leng] = bstr.charCodeAt(leng) //返回指定位置的字符的 Unicode 编码
        }
        var blob = new Blob([u8arr],{type:contentType})
        var blobImg = {}
        blobImg.url = URL.createObjectURL(blob)  //创建URL,
        blobImg.name = new Date().getTime() + '.jpeg'
        return blob
    }

    /**
     * 读取FileEntry对象,并进行下载操作
     * @param fs
     * @param entries
     */
    function readFiles(fs,entries){
        if (entries.length > 0) {
            // entry & add record file(s) to play list
            for (var i = 0; i < entries.length; i++) {
                var entry = entries[i];
                if (entry.isFile) {
                    console.log(entry)
                    download_file(fs, entry.name)
                }
            }
        }
    }


    /**
     * 使用a标签,对文件进行自动下载
     * @param fs
     * @param fileName
     */
    function download_file(fs,fileName) {
        if (null == fs)
            return;

        // 创建a标签,用于触发下载
        const a = document.createElement('a');
        // 将 a 标签的 download 属性设置为要下载的文件名
        a.download = fileName || 'image';

        fs.root.getFile('images/' + fileName, {}, function(fileEntry) {
            fileEntry.file(function (file) {
                a.href = window.URL.createObjectURL(file);
                // 将a标签暂时添加到 body 中,触发下载
                document.body.appendChild(a);
                a.click();
                // 下载完成后,将 a 标签从 body 中移除
                document.body.removeChild(a);
            });
        });
    }

    /**
     * 加载本地目录下的所有文件
     */
    function loadFiles() {
        window.webkitRequestFileSystem(window.PERSISTENT, 16 * 1024*1024*1024, function (f) {
            console.log(f);
            f.root.getDirectory("images",{create:true},function(dirEntry){
                var dirReader = dirEntry.createReader();
                dirReader.readEntries(function(entries) {
                    console.log(f, entries)
                    readFiles(f, entries)
                })
            })
        })
    }

    /**
     * 保存文件到本地指定目录下
     */
    function saveFile(){

        window.webkitRequestFileSystem(window.PERSISTENT, 16 * 1024*1024*1024, function (f) {
            console.log(f);
            f.root.getDirectory("images",{create:true},function(g){
                console.log(g)
            })

            for (i = 0;i<10;i++){
                f.root.getFile("images/" + i + ".jpg" ,{create:true},function(h){
                    console.log(h)
                    h.createWriter(function(j){
                        console.log(j)
                        var blob = base64ToBlob(base64ImageData, 'image/jpeg')
                        j.write(blob);
                    });
                })
            }

        })
    }

    function saveLocalFile(){
        console.log("保存文件到本地")
        saveFile()
    }

    function downloadLocalFile(){
        console.log("下载文件")
        loadFiles()
    }


</script>

<div>
    <input type="button" value="添加文件到本地" onclick="saveLocalFile()">
    <input type="button" value="下载文件列表" onclick="downloadLocalFile()">
</div>
</body>
</html>

效果展示:

image-20230710183904334

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/938209.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

战略形成是权力妥协的过程,江湖,政治是常态

战略权力派&#xff1a;战略形成是各种权力妥协的过程【安志强趣讲270期】 趣讲大白话&#xff1a;有人的地方就有政治 **************************** 有人的地方就有江湖 有组织的地方就有政治 公司的战略是各种人的权力博弈的产物 围观权力&#xff1a;就是组织内部 宏观权力…

点可云进销存开源系统V6.0.1 ERP系统进销存源码仓库管理

介绍 点可云进销存系统&#xff0c;基于thinkphplayui开发。 功能包含&#xff1a;采购、销售、零售、多仓库管理、财务管理等功能 和超详细的报表功能&#xff08;采购报表、销售报表、零售报表、仓库报表、资金报表等&#xff09; 软件架构 thinkphplayui 功能概览 购货 -购…

【深度学习】实验03 特征处理

文章目录 特征处理标准化归一化正则化 特征处理 标准化 # 导入标准化库 from sklearn.preprocessing import StandardScalerfrom matplotlib import gridspec import numpy as np import matplotlib.pyplot as plt import warnings warnings.filterwarnings("ignore&quo…

利用阿里云服务器公网IP+FRP搭建内网穿透

1 必要条件&#xff1a; 一台公网IP服务器&#xff0c;这里采用阿里云ECS服务器。 此处将IP定义为:serverA-IP 2 服务器下载代码&#xff1a; # mkdir /data # cd /data # git clone https://github.com/fatedier/frp.git # cd frp3 编译代码 编译需要时间 # make go fmt .…

【Redis从头学-13】Redis哨兵模式解析以及搭建指南

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Re…

第4节——react 中如何编写样式

一、内联样式 内联样式就是在JSX元素中&#xff0c;直接定义行内的样式。与普通的HTML内联样式唯一的区别就是&#xff0c;JSX中的内联样式是一个对象&#xff0c;而不是一个字符串。 import React from reactexport default class LearnStyle extends React.Component {rend…

CSS中如何实现文字阴影效果(text-shadow)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 实现思路⭐ 示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前…

【Linux操作系统】Linux系统编程中的读写锁

文章目录 1.读写锁的原理2.提供的操作函数示例代码4.示例解释 1.读写锁的原理 读写锁&#xff08;Read-Write Lock&#xff09;是一种特殊的锁机制&#xff0c;用于在多线程环境下对共享资源进行读写操作。与互斥锁不同&#xff0c;读写锁允许多个线程同时读取共享资源&#x…

多图详解VSCode搭建Java开发环境

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

JavaSE学习——异常

目录 一、异常概述 二、异常的体系结果 二、异常的处理&#xff1a;抓抛模型 三、try-catch-finally的使用 四、throws 异常类型 的使用 五、开发中如何选择使用try-catch-finally还是使用throws&#xff1f; 六、自定义异常 自定义异常步骤&#xff1a; 七、总结&a…

Android 13 - Media框架(7)- NuPlayer::Source

Source 在播放器中起着拉流&#xff08;Streaming&#xff09;和解复用&#xff08;demux&#xff09;的作用&#xff0c;Source 设计的好坏直接影响到播放器的基础功能&#xff0c;我们这一节将会了解 NuPlayer 中的通用 Source&#xff08;GenericSource&#xff09;关注本地…

MySQL数据库——多表查询(1)-多表关系(一对多、多对对多、一对一)、多表查询概述(概念、笛卡尔积、分类)

目录 概述 多表关系 一对多&#xff08;多对一&#xff09; 多对多 一对一 多表查询概述 概念 笛卡尔积 分类 多表查询 多表关系多表查询概述内连接外连接自连接子查询多表查询案例 概述 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及…

框架分析(6)-Ruby on Rails

框架分析&#xff08;6&#xff09;-Ruby on Rails 专栏介绍Ruby on Rails核心概念以及组件讲解MVC架构模式约定优于配置强大的ORM支持自动化测试丰富的插件生态系统RESTful路由安全性总结 优缺点优点快速开发简单易学MVC架构强大的ORM支持大量的插件和Gem支持 缺点性能问题学习…

【Go 基础篇】Go语言数组遍历:探索多种遍历数组的方式

数组作为一种基本的数据结构&#xff0c;在Go语言中扮演着重要角色。而数组的遍历是使用数组的基础&#xff0c;它涉及到如何按顺序访问数组中的每个元素。在本文中&#xff0c;我们将深入探讨Go语言中多种数组遍历的方式&#xff0c;为你展示如何高效地处理数组数据。 前言 …

从零做软件开发项目系列之六——软件测试

前言 软件测试是指在软件开发过程中对软件系统进行验证和验证的过程。它的目的是确保软件能够按照设计要求正常运行&#xff0c;同时检测和纠正可能存在的缺陷和问题。软件测试有助于提高软件质量、可靠性和用户满意度。 1 测试阶段 软件测试阶段是软件开发过程中的重要环节…

【电源专题】18650圆柱电芯内部结构及器件

18650圆柱锂离子电池是一种直径为18mm、高度为65mm的锂离子电池,它最大的特点是拥有非常高的能量密度,它是比较成熟的锂离子电池,各方面系统质量稳定性较好,广泛适用于10千瓦时左右的电池容量场合,例如在、在手机、笔记本电脑等小型电器上。 常见的18650电池分为锂离子电池…

【IMX6ULL驱动开发学习】09.Linux之I2C驱动框架简介和驱动程序模板

参考&#xff1a;Linux之I2C驱动_linux i2c驱动_风间琉璃•的博客-CSDN博客​​​​​​ 目录 一、I2C驱动框架简介 1.1 I2C总线驱动 1.2 I2C设备驱动 二、I2C总线-设备-驱动模型 2.1 i2c_driver 2.2 i2c_client 2.3 I2C 设备数据收发和处理 三、Linux I2C驱动程序模板…

自然语言处理: 第十章GPT的API使用

理论基础 现在的以GPT为首的生成类模型&#xff0c;它拥有对话的能力&#xff0c;它会根据你输入的暗示(prompt)或者指令(instruct)生成对应的回答。所以&#xff0c;不同的输入会导致不同的输出(其实由于chatgpt最终生成的答案是beam_search 以及随机采样的机制&#xff0c;所…

自定义Chronometer实现定时器

概述 自定义Chronometer实现定时器,引用方便&#xff0c;操作简单。 详细 前言 在Android开发过程中&#xff0c;计时控件是经常回使用到的&#xff0c;在Android控件库中有一个能快捷实现计时功能的控件&#xff0c;它就是Chronometer&#xff0c;今天我们基于它自定义实现…