File System Access API 浅析

news2024/7/4 5:03:38

前言

最近在用pythontkinter GUI库)做一个小工具时,选择文件后可以获得其真实路径。而前端(浏览器)出于安全和性能等方面的考虑,对文件的操作是非常局限的。

HTML5标准的File API之前,纯前端几乎都无法完成图片预览的功能。即使有了File API,我们也不能直接读取本地文件,创建文件也只能通过下载的方式,更别说修改文件内容了。

如果我们想实现下载的时候弹出文件框,让用户自行选择保存的位置,又该怎么处理?

借此,梳理一下前端文件相关的方式方法,开帖记录(本篇为公司内部分享文章,同步发布到相关平台)。

File API

可以让用户在网页中选择本地文件,并读取这些文件信息。

最常见的可能就是上传功能了,一般通过htmlinput标签实现,示例如下

<input type="file" multiple onchange="fileChange()">

function fileChange(){
	console.log(event.target.files);
}

input-file通常搭配下面两个参数使用

属性描述
acceptMIME_TYPE规定通过文件上传来提交的文件的类型
multiple-是否可多选

读取到的File信息一般包括

属性描述
name文件名
size文件大小
type文件MIME类型
lastModified文件上次修改时间(时间戳)
lastModifiedDate文件上次修改时间(Date对象)

FileReader

一般用于读取文件,参数为File对象或Blob对象,示例如下

<input type="file" onchange="fileChange()"/>

function fileChange(){
    const reader = new FileReader();
    reader.readAsDataURL(event.target.files[0]);
    reader.onload = function(e){
        console.log(e.target.result)
    }          
}

API有如下方法和事件

方法描述
abort()终止读取操作
readAsArrayBuffer(Blob|File)返回ArrayBuffer对象
readAsBinaryString(Blob|File)返回二进制字符串
readAsDataURL(Blob|File)返回Base64编码的对象
readAsText(Blob|File,encoding)返回文本字符串
onloadstart读取操作开始时调用
onprogress读取数据过程中周期性调用
onabort读取操作被中止时调用
onerror读取操作发生错误时调用
onload读取操作成功完成时调用
onloadend读取操作完成时调用,无论成功,失败或取消

简单示例如下
在这里插入图片描述

File System API

HTML5新增了文件系统API,可以创建一个独立的沙箱文件系统,让开发者在此系统中进行创建、读写、移动、删除、索引文件等操作。它基于文件写入 APIFile Writer API),可以用于缓存和处理大量数据。

当然,此特性是非标准的(目前只有Google Chrome支持),尽量不要在生产环境中使用。

应用场景

应用场景

  • 上传中断(网络问题或浏览器崩溃等)后重新上传
  • 应用后台下载资源,无需等待完成才能进行后续动作
  • 配合IndexedDb或其它缓存方案,实现高效读写
  • 其它

一些说明

  • 此沙箱是一个虚拟的文件系统,不能读写用户硬盘中的文件
  • 浏览器会给每一个应用限定配额并分配存储(防止应用占满磁盘,可使用配额管理API申请合理的空间)
  • 文件系统支持异步和同步方法
  • 可以配合XMLHttpRequestDrop APIWeb Workerinput-file等使用,传递文件对象
  • 同源策略

下面介绍相关API和简单使用示例

基本使用

FileEntry/DirectoryEntry

沙盒环境的文件通过FileEntry句柄(目录为DirectoryEntry)操作

属性/方法说明
name操作对象名称
isFile操作对象是否为文件
isDirectory操作对象是否为目录
fullPath完整路径,文件系统的绝对路径
filesystem文件系统对象(name/root),详见FileSystem
getMetadata获取文件/目录信息
moveTo移动文件/目录
copyTo拷贝文件/目录
toURL完整路径,文件系统的绝对路径
remove删除文件/目录
getParent获取父目录
file获取文件数据对象(FileEntry)
createWriter用于写入文件(FileEntry)
createReader用于读取目录(DirectoryEntry)
getDirectory创建目录(DirectoryEntry)
getFile创建文件(DirectoryEntry)
removeRecursively递归删除目录(DirectoryEntry)

查询配额

首先我们需要查询系统可使用的临时/持久空间大小

// 持久磁盘配额
navigator.webkitPersistentStorage.queryUsageAndQuota(successCallback,errorCallback)
// 临时磁盘配额
navigator.webkitTemporaryStorage.queryUsageAndQuota(successCallback,errorCallback)
属性说明
successCallback成功回调
errorCallback失败回调
// 查询临时配额
navigator.webkitTemporaryStorage.queryUsageAndQuota ( 
    function(usedBytes, grantedBytes) {
        console.log('已使用:',usedBytes,' 总量:',grantedBytes);
    }, 
    function(e) { console.log('Error', e);  }
);

申请配额

查询成功后,可以根据实际需求申请合适的配额空间

navigator.webkitTemporaryStorage.queryUsageAndQuota(requestedBytes,successCallback,errorCallback)
属性说明
requestedBytes申请配额空间大小(字节)
successCallback成功回调
errorCallback失败回调
// 申请5M空间
var requestedBytes = 1024*1024*5;
navigator.webkitTemporaryStorage.requestQuota (
    requestedBytes,
    function(grantedBytes) {  
        console.log('请求成功的空间: ', gengerate(grantedBytes));
    }, 
    function(e) {
        console.log('Error', e);
    }
);

初始化文件系统

在请求配额成功后,可以请求访问文件系统

window.webkitRequestFileSystem(type,size,successCallback,errorCallback)
属性说明
typewindow.TEMPORARY:临时存储空间;window.PERSISTENT:永久存储空间
size需要用于存储的大小(字节)
successCallback成功回调
errorCallback失败或发生错误时回调
window.webkitRequestFileSystem(window.PERSISTENT, 1024*1024*5, initFsHanlder, errorHandler);
// 成功回调
function initFsHanlder(fs) {
    console.log(fs);
}
// 错误回调
function errorHandler(e) {
    console.log('错误:',e)
}

使用文件

请求文件系统成功后,我们可以创建文件

// 创建文件
fs.root.getFile(name,opts,successCallback,errorCallback)
属性说明
name文件名/目录名
options(create/exclusive)文件操作的参数
successCallback(fileEntry|dirEntry)成功回调
errorCallback失败或发生错误时回调
创建文件

使用getFile查找或创建文件,成功回调传递FileSystem对象(FileEntry)。下面示例为在根目录创建test.txt文本。

function initFsHanlder(fs) {
    // 直接创建文件
    // create:默认false,如果目标文件不存在是否创建
    // exclusive:默认false,需配合create:true使用,如果目标文件不存在则创建,存在则覆盖
    fs.root.getFile('test.txt', { create: true }, function (fileEntry) {
        console.log('创建文件成功:',fileEntry);
    }, errorHandler);
}

在这里插入图片描述

读写文件

这里我们创建test.txt文本,并向其中添加(追加)自定义内容。

// 创建文件
function createFile(fs, filename) {
    fs.root.getFile(filename, { create: true },function (fileEntry) {
        console.log(`创建文件${filename}成功`)
    }, errorHandler);
}
// 读取内容
function readFile(fileEntry){
    if (fileEntry.isFile) {
        fileEntry.file(function (file) {
            var reader = new FileReader();
            reader.onloadend = function () {
                console.log('读取文件成功:',reader)
            }
            reader.readAsText(file);
        });
    }
}
// 写入内容
function addContent(fs,filename) {
    fs.root.getFile(filename, { create: true },function (fileEntry) {
        if (fileEntry.isFile) {
            fileEntry.createWriter(function (fileWriter) {
                // 写入内容(File或Blob对象)
                var blob = new Blob(['hello world,this is my first try'], {
                    type: 'text/plain'
                });
                // 写入结束
                fileWriter.onwriteend = function (e) {
                    console.log('写入文件结束',e);
                }
                // 写入错误
                fileWriter.onerror = function (e) {
                    console.log('写入异常:',e);
                }
                // 移动(光标)到指定位置,追加内容
                // fileWriter.seek(fileWriter.length);
                // 执行写入
                fileWriter.write(blob);
            }, errorHandler);
        }
    }, errorHandler);
}

在这里插入图片描述

使用目录

请求文件统成功后,我们可以创建文件(方法与文件一致,可参考使用文件部分)

// 创建目录
fs.root.getDirectory(name,opts,successCallback,errorCallback)
创建目录

使用getDirectory查找或创建目录,成功回调传递FileSystem对象(DirectoryEntry )。下面示例为在根目录创建testDir目录,并在testDir目录下创建dir.txt文本

function initFsHanlder(fs) {
    // 直接创建目录
    // create:默认false,如果目录不存在是否创建
    // exclusive:默认false,需配合create:true使用,如果目录不存在则创建,存在则覆盖
    fs.root.getDirectory('testDir', { create: true }, function (dirEntry) {
        console.log('创建目录成功:', dirEntry)
    }, errorHandler)
}

在这里插入图片描述

创建子目录

需要注意的是不能直接创建其直接父目录不存在的目录,一般通过递归依次创建各级目录,示例如下。

// 错误示例如下
fs.root.getDirectory('父文件夹/子文件夹', { create: true }, function (dirEntry) {
    console.log('创建目录成功:', dirEntry)
}, errorHandler)

// 正确示例如下
function createDir(rootDirEntry, folders) {  
    rootDirEntry.getDirectory(folders[0], {create: true}, function(dirEntry) {  
        if (folders.length) {  
            createDir(dirEntry, folders.slice(1));  
        }  
    }, errorHandler);  
};
var folderPath = '我的文档/资料/web前端';
createDir(fs.root, folderPath.split('/'));

在这里插入图片描述

读取目录

需要注意的是只能直接读取当前目录的直属子目录,一般通过递归依次读取各级目录,示例如下(示例中提前创建了一些目录和文件)。

// 读取文件夹和文件
function readFolder(foldername){
    fs.root.getDirectory(foldername, { create: true }, function (dirEntry) {
        if(dirEntry.isDirectory){
            var reader = dirEntry.createReader();
            reader.readEntries(function(e) {  
                if (e.length) { 
                    let list = Array.prototype.slice.call(e, 0); 
                    list.map(v=>{
                        if(v.isDirectory){
                            readFolder(v.fullPath,v.name);
                        }
                    })  
                }
            }, errorHandler);  
        }
    }, errorHandler)
}

在这里插入图片描述

其它操作

文件拷贝、删除、移动
fs.root.getDirectory('testDir', { create: true }, function (dirEntry) {
    console.log('创建目录testDir成功,路径:', dirEntry.fullPath)
    // 拷贝文件并且重命名
    fs.root.getFile('test.txt', { create: true }, function (fileEntry) {
        console.log('创建文件test.txt成功,路径:', fileEntry.fullPath);
        fileEntry.copyTo(
            dirEntry,
            'test_copy.txt',
            (e) => {  console.log('拷贝文件test.txt成功,新路径:', e.fullPath) },
            (e) => { console.log('拷贝文件test.txt失败:', e) }
        );
    }, errorHandler);
    // 移动文件并且重命名
    fs.root.getFile('test1.txt', { create: true }, function (fileEntry) {
        console.log('创建文件test1.txt成功,路径:', fileEntry.fullPath);
        fileEntry.moveTo(
            dirEntry,
            'test1_move.txt',
            (e) => {  console.log('移动test1.txt成功,新路径:', e.fullPath) },
            (e) => { console.log('移动test1.txt失败:', e) }
        );
    }, errorHandler);
    // 删除文件
    fs.root.getFile('test2.txt', { create: true }, function (fileEntry) {
        console.log('创建文件test2.txt成功,路径:', fileEntry.fullPath);
        fileEntry.remove(
            (e) => { console.log('删除test2.txt成功:', e) },
            (e) => { console.log('删除test2.txt失败:', e) }
        );
    }, errorHandler);
}, errorHandler)

在这里插入图片描述

目录拷贝、删除、移动
// 普通删除:若文件夹下存在文件,则无法删除
fs.root.getDirectory('testDir/childDir', { create: true }, function (dirEntry) {
	dirEntry.remove(function () {
        console.log(`删除目录${dirEntry.fullPath}成功`);
    }, errorHandler);
}, errorHandler)

// 递归删除:会删除所有子文件和子文件夹
fs.root.getDirectory('testDir/childDir', { create: true }, function (dirEntry) {
	dirEntry.removeRecursively(function () {
        console.log(`删除目录${dirEntry.fullPath}成功`);
    }, errorHandler);
}, errorHandler)

// 移动、拷贝目录
fs.root.getDirectory('testDir/childDir1', { create: true }, function (dirEntry) {
    console.log('创建目录成功,路径:',dirEntry.fullPath)
    fs.root.getDirectory('testDir/childDir2', { create: true }, function (dirEntry2) {
        console.log('创建目录成功,路径:',dirEntry2.fullPath)
        // 拷贝到根目录,并重命名
        dirEntry.copyTo(fs.root, 'childDir1_copy', function (dirEntiry3) {
            console.log('复制目录成功,新路径:',dirEntiry3.fullPath);
        }, errorHandler);
        // 将childDir1移动到childDir2下,并重命名
        setTimeout(()=>{
            dirEntry.moveTo(dirEntry2, 'testDir_move', function (dirEntry4) {
                console.log('移动目录成功,新路径:', dirEntry4.fullPath);
            }, errorHandler);
        },2000)
    }, errorHandler)
}, errorHandler)

在这里插入图片描述

到这里我们就使用File System API完成了创建、写入、读取、拷贝、删除文件(夹)等基本操作。关于上面的应用场景的具体实现,这里不做赘述,感兴趣的可以自行查阅相关资料。

回到一开始我们说的问题:此沙箱是一个虚拟的文件系统,不能读写用户硬盘中的文件,也无法做到一些自定义交互效果。接下来我们来看一种更加稳妥且安全的交互API

File System Access API

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

相比于File System API,它提供了更强大的文件读写能力。

应用场景

  • 将文件从本地文件系统上传到Web应用程序
  • Web应用程序中的数据写入到本地文件系统中
  • 在用户的本地文件系统上创建、重命名和删除文件
  • 读取本地文件系统上的文件内容
  • 其它…

一些说明

  • 需用户授权访问相应的文件或目录
  • 文件系统支持异步方法
  • 同源策略

下面介绍相关API和简单使用示例

基本使用

此API提供3个基本(异步)方法,可配合async/await/then使用。

  • 打开文件选取窗口

    window.showOpenFilePicker

  • 打开文件保存窗口

    window.showSaveFilePicker

  • 打开目录选取窗口

    window.showDirectoryPicker

FileSystemFileHandle/FileSystemDirectoryHandle

选择文件或目录后,可以获取到FileSystemFileHandle(目录为FileSystemDirectoryHandle),后续操作由这个句柄进行(继承自FileSystemHandle)

属性/方法说明
kind类型(file/directory)
name文件名/文件夹名
isSameEntry比较两个句柄关联的文件或目录是否匹配
queryPermission查询当前句柄的权限状态
requestPermission请求文件句柄的读/写权限
remove从文件系统中删除该句柄对应的文件或目录
move移动文件(FileHandle)
getFile获取File对象(FileHandle)
createWritable创建写入File对象(FileHandle)
entries返回[key,value]形式的异步迭代器(DirectoryHandle)
values返回键值异步迭代器(DirectoryHandle)
getDirectoryHandle返回指定名称的目录的句柄(DirectoryHandle)
getFileHandle返回指定名称的文件的句柄(DirectoryHandle)
removeEntry删除指定名称的文件或目录(DirectoryHandle)
resolve返回由从父句柄到指定子项的目录名数组,子项的名称作为最后一个数组项(DirectoryHandle)

选择文件

window.showOpenFilePicker():显示一个文件选择器,允许用户选择一个或多个文件并返回这些文件的句柄。

属性说明
multiple是否允许多选(默认false)
excludeAcceptAllOption是否排除“接受所有”选项(默认 false)
types允许选择的文件类型(description:描述/accept:MIME类型)
async function openFile() {
    let fileHandles = await window.showOpenFilePicker({
        multiple: true,
        excludeAcceptAllOption: true,
        types: [
            {
                description: "选择图片",
                accept: {
                    "image/*": [".png", ".gif", ".jpeg", ".jpg"],
                },
            },
        ],
    });
    console.log(fileHandles);
};

在这里插入图片描述

选择文件回调中可以获取File对象(后续可以按照之前的方式处理即可)

async function openFileAndRead() {
    let [fileHandle] = await window.showOpenFilePicker({
        multiple: false, // 取消多选
        excludeAcceptAllOption: true,
        types: [
            {
                description: "选择文本文件",
                accept: {
                    "text/plain": [".txt"],
                },
            },
        ],
    });
    let file = await fileHandle.getFile();
    console.log(file);
    let content = await file.text();
    console.log(`打开文件: ${fileHandle.name}\n文件内容:\n${content}`);
};

在这里插入图片描述

选择目录

window.showDirectoryPicker():显示一个目录选择器,允许用户选择一个目录。

属性说明
id通过指定 ID,浏览器能够记住不同 ID 所对应的目录。当使用相同的 ID 打开另一个目录选择器时,选择器会打开相同的目录
mode默认 read,可对目录进行只读访问。 readwrite 可对目录进行读写访问
startIn用于指定选择器的起始目录。可以是FileSystemHandle对象或者常见目录(如:“desktop”、“documents”、“downloads”、“music”、“pictures”、“videos”)
// 默认从“下载”目录打开
async function openDir() {
    let dirHandle = await window.showDirectoryPicker({
        mode: "read",
        startIn: "downloads",
    });
    console.log(dirHandle);
}

在这里插入图片描述

保存文件

window.showSaveFilePicker():显示一个文件选择器,允许用户保存一个文件。可以选择一个已有文件覆盖保存,也可以新建一个文件。

属性说明
excludeAcceptAllOption是否排除“接受所有”选项(默认 false)
suggestedName建议的文件名称
types允许保存文件类型的数组(description:描述/accept:MIME类型)
// 写入文本到下载目录
async function writeFile() {
    // 写入文本到下载目录
    let writeHandle = await window.showSaveFilePicker({
        suggestedName: "写入测试.txt", // 待写入的文件名
        startIn: "downloads", //默认打开下载目录
    });
    const writableStream = await writeHandle.createWritable();
    // 写入文件
    await writableStream.write({
        type: 'write',
        position: 4,
        data: "这里是通过API写入的文本内容,从第4位开始"
    });
    // 关闭流
    await writableStream.close();
    console.log(`保存成功: ${writeHandle.name}`);
}

在这里插入图片描述

当然,也可以配合Web WorkercreateSyncAccessHandle来实现高性能的文件读写操作,这里不做示例说明。

其它示例

API有很多有意思的方法,下面给出几个简单示例。

1、强制选择指定类型文件

一般在使用input-file时,即使我们指定上传文件类型,还会有一个默认的“所有文件”的选项,可以选择其它类型的文件

在这里插入图片描述

通过API调用,设置excludeAcceptAllOption则可手动控制隐藏这个选项

在这里插入图片描述

2、操作本地文件

我们可以模拟以下功能场景:

  • 选择一个目录,自动写入文件(夹)
  • 选择一个文件,再选择一个目录,自动将文件移动到所选目录中

首先在桌面创建两个文件夹testDirtestDir2备用。

选择testDir文件夹,在其中自动创建一个文件夹和文本文件(create:true表示不存在则创建)

async function createDir() {
    let dirHandle = await window.showDirectoryPicker({
        mode: "read",
        startIn: 'desktop'
    });
    console.log(dirHandle);
    // 创建默认文件夹
    const dirName = "默认文件夹";
    const subDir = await dirHandle.getDirectoryHandle(dirName, { create: true });
    // 创建默认文件
    const fileName = "默认文本.txt";
    const subFile = await dirHandle.getFileHandle(fileName, { create: true })
    }

在这里插入图片描述

选择testDir文件夹,并读取其内容句柄(刚才创建的文件和文件夹)

async function readDir() {
    let dirHandle = await window.showDirectoryPicker({
        mode: "read"
    });
    for await (const item of dirHandle.values()) {
        console.log(item)
    }
}

在这里插入图片描述

移动testDirtxt文本到testDir2文件夹中

async function moveTxt() {
    // 选择目标文件
    let [fileHandle] = await window.showOpenFilePicker({
        multiple: false,
        excludeAcceptAllOption: true,
        types: [
            {
                description: "选择文本",
                accept: {
                    "text/plain": [".txt"],
                },
            },
        ],
    });
	// 选择目标目录
    let dirHandle = await window.showDirectoryPicker({
        mode: "readwrite"
    });
    fileHandle.move(dirHandle)
}

在这里插入图片描述

选择testDir,删除刚才创建的文件和文件夹

async function deleteDir() {
    let dirHandle = await window.showDirectoryPicker({
        mode: "read"
    });
    for await (const item of dirHandle.values()) {
        console.log(item)
        if(item.kind == 'file'){
            // 删除文件
            dirHandle.removeEntry(item.name);
            // 也可以使用如下方法=>fileHandler.remove(fileHandler)
            // item.remove(item)
        }else{
            // recursive:文件夹递归删除所有子目录和文件
            dirHandle.removeEntry(item.name, { recursive: true });
        }
    }
}
3、自定义换肤

一般多主题方案是固定的(由最终产品提供固定的几种配色或布局),灵活一点的可能会允许用户针对基础配色进行调整,这种需要依赖前后端配置。

如果我们不依赖后台,而是考虑通过纯前端方式,在本地生成相应的“皮肤文件”,允许用户选择这些文件从而动态完成应用多主题,是不是显得(闲的)高大上呢?

这里不做赘述,有兴趣的可以继续深究。

兼容性

可以看到现代浏览器是部分支持(或支持部分API)。

在这里插入图片描述

后记

  • 浏览器对于本地文件的操作一直都是很局限的,其实也限制了一部分前端的发展,很多功能需要依赖后台或者其他插件
  • File System API只有Chrome支持实现,其最大贡献在于突破原有定式,赋予浏览器直接操作文件和目录的能力,这一步如果成功,可极大提升浏览器在应用系统中的地位(然而步子太大,GG了)
  • File System Access API真正意义上让浏览器可以操作本地文件。但出于安全考虑,浏览器不允许我们访问一些敏感目录(如包含系统文件的目录或者组策略不允许访问的目录)
  • File System Access API提供了更加强大的Web应用程序功能。我们可以利用它完成如文本/图形在线编辑器、导入导出优化、加载用户本地脚本、大文件切片缓存等功能
  • File System Access API功能很强大,但兼容性是当下比较现实的问题。基于安全和性能考量,各浏览器厂商并未完全纳入自有规范中,但可以看到主流浏览器都在朝着这个方向去发展(兼容了部分操作)
  • 为了方便理解,以上给出的示例都是比较简单的,实际上还有很多复杂的API和功能,感兴趣的话可自行查阅相关资料(有封装好的三方库,如browser-fs-access等)
  • 当然我们还是期待此标准能够尽快沉淀和完善起来,可以给前端(浏览器)的生态带来一些变化,也许我们可以摆脱inputa标签的束缚,做出更灵活的交互

参考资料

File_System_Access_API

file-system-access

HTML5 本地文件操作之FileSystemAPI实例

浏览器读写本地文件 File System Access API

FileSystemHandle

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

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

相关文章

【Redis】高可用之复制(replica)

本文是Redis系列第4篇&#xff0c;前3篇欢迎移步 【Redis】不卡壳的 Redis 学习之路&#xff1a;从十大数据类型开始入手_AQin1012的博客-CSDN博客关于Redis的数据类型&#xff0c;各个文章总有些小不同&#xff0c;我们这里讨论的是Redis 7.0&#xff0c;为确保准确&#xff…

Raft算法之Leader选举

Raft算法之Leader选举 一、Leader选举概述 Raft 使用心跳&#xff08;heartbeat&#xff09;触发Leader选举。当服务器启动时&#xff0c;初始化为Follower。Leader向所有Followers周期性发送heartbeat。如果Follower在选举超时时间内没有收到Leader的heartbeat&#xff0c;就…

图像视频基础

参考学习资料&#xff1a;https://blog.csdn.net/qq_28258885/article/details/116192244 文章目录 图像颜色深度分辨率 视频帧率比特率帧类型消除冗余的方法时间冗余&#xff08;帧间预测&#xff09;空间冗余&#xff08;帧内预测&#xff09; 视频编码器1.分区2.预测3.转换…

软件测试基础教程学习4

文章目录 软件测试技术和方法4.1 静态测试和动态测试4.2 黑盒测试和白盒测试概述4.3 黑盒测试技术4.3.1 等价类划分4.3.2 边值分析4.3.3 因果图法4.3.4 正交实验设计法4.4.5 决策表驱动测试4.5.6 错误推荐法 4.4 白盒测试技术4.4.1 程序结构分析测试4.4.2 逻辑覆盖测试4.4.3 路…

JSP页面跳转刷新

问题: 当前的jsp页面触发ajax请求后,能够获得新的相应页面,但是浏览器上展示的依然是老的页面,数据不刷新 尝试使用页面重定向依然无效, 最后使用js的window.location.href, 让浏览器的页面url 重加载才ok function submitDate() {var date1 document.getElementById("d…

【uniapp】uniapp反向代理解决跨域问题(devServer)

背景介绍 前段时间&#xff0c;在拿uniapp开发的时候&#xff0c;出现了跨域问题&#xff0c;按理说跨域应该由后端解决&#xff0c;但既然咱前端可以上&#xff0c;我想就上了&#xff08;顺手装个13&#xff09; 首先介绍什么是跨域 出于浏览器的同源策略&#xff0c;在发…

docker-使用harbor搭建私有仓库

前提 安装docker-ce 安装docker-compose 安装 安装docker-ce # step 1: 安装必要的一些系统工具 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce…

金蝶云星空无需代码连接钉钉考勤的方法

金蝶云星空用户使用场景&#xff1a; 企业的销售渠道人员出差之前需要在金蝶云星空上提交出差申请单&#xff0c;并等待审批通过&#xff1b;每当销售任务完成&#xff0c;金蝶云星空上的审批通过后&#xff0c;需要人力在考勤系统中手动修改考勤信息。看似比较简单的流程&…

基于Java+SpringBoot+vue的汽车改装方案网站设计与实现

博主介绍&#xff1a;✌擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案…

Bitbucket 新版本的安全限制

新版本的安全限制 是继续按照他给的第二个链接进入Bitbucket仓库后台添加App密码&#xff0c;也就是每个仓库需要单独的秘密码&#xff0c;这样的话就更加安全。 生产新密码&#xff1a; 这一坨务必要妥善保存&#xff0c;因为一旦点了关闭之后你就再也没有机会看到这个密码了…

Hive一分钟

分区和分桶 1.分区表是将大的表文件划分成多个小文件以利于查询&#xff0c;但是如果数据分布不均衡&#xff0c;也会影响查询效率。 2.桶表可以对数据进行哈希取模&#xff0c;目的是让数据能够均匀的分布在表的各个文件中。 3.物理上&#xff0c;每个桶就是表和分区目录里的…

14个在你的WordPress网站上使用OpenAI的最好方法(2003)

您是否想知道如何在您的WordPress网站上使用OpenAI和ChatGPT&#xff1f; OpenAI可以提供一切帮助&#xff0c;从为您的帖子生成元描述到撰写电子邮件销售文案。您可以在您的WordPress网站上使用OpenAI来节省时间、降低成本、改善您的搜索引擎优化和工作流程&#xff0c;并发展…

推荐大型电商项目【谷粒商城】

谷粒商城项目是尚硅谷研究院最新推出的完整大型分布式架构电商平台&#xff0c;技术全面、业务深入&#xff0c;全网无出其右。 技术涵盖&#xff1a;微服务架构分布式全栈集群部署自动化运维可视化CICD&#xff0c;对标阿里P6/P7&#xff0c;冲击40-60w年薪。 项目由业务集群…

吐血整理!可免费使用的国产良心软件分享,几乎满足你办公需求

在这个信息化时代&#xff0c;软件已经成为我们办公和生活的必备工具。然而&#xff0c;市面上的大部分国产软件都需要付费才能使用&#xff0c;给我们的经济负担增加了不少。幸运的是&#xff0c;国内有一些良心软件&#xff0c;它们质量上乘&#xff0c;功能强大&#xff0c;…

myCobot机器人ChatGPT应用:设计原则和模型能力

我们将 ChatGPT 的功能扩展到机器人&#xff0c;并通过语言直观地控制机器人手臂、无人机和家庭助理机器人等多个平台。 你有没有想过用你自己的话告诉机器人该怎么做&#xff0c;就像你对人类一样&#xff1f;只是告诉你的家庭助理机器人&#xff1a;“请加热我的午餐”&…

SpringBoot+ Dubbo + Mybatis + Nacos +Seata整合来实现Dubbo分布式事务

1.简介 “ 本文主要介绍SpringBoot2.1.5 Dubbo 2.7.3 Mybatis 3.4.2 Nacos 1.1.3 Seata 0.8.0整合来实现Dubbo分布式事务管理&#xff0c;使用Nacos 作为 Dubbo和Seata的注册中心和配置中心,使用 MySQL 数据库和 MyBatis来操作数据。 ” 如果你还对SpringBoot、Dubbo、Nacos…

数据湖真的能取代数据仓库吗?【SNP SAP数据转型 】

数据湖和数据仓库的存在并不冲突&#xff0c;也并不是取代的关系&#xff0c;而是相互的融合关系。 数据湖是近两年中比较新的技术在大数据领域中&#xff0c;对于一个真正的数据湖应该是什么样子&#xff0c;现在对数据湖认知还是处在探索的阶段&#xff0c;像现在代表的开源产…

(五)复函数积分的定义与性质

本文内容主要如下&#xff1a; 1. 复积分的概念1.1. 复积分的定义1.2. 复积分的存在性与计算1.3. 一个圆周上的重要积分公式1.4. 复积分的基本性质 1. 复积分的概念 1.1. 复积分的定义 定义&#xff1a; 如图&#xff0c;C为平面上一条光滑的简单曲线: z z ( t ) x ( t )…

GAD7980/CL1680/AD7980详解与开发说明

目录 1 概述2 GAD7980简介3 用法时序4 参数计算与参数解释4.1 采样率4.2 转换时间4.3 采集时间5 采样数值折算6 设计注意事项7 代码demo 1 概述 本文用于讲述GAD7980的功能与用法&#xff0c;以及其中一些参数的计算方法&#xff0c;用法时序&#xff0c;输出数值等等&#xf…

chatglm+langchain

目录 chatglmlangchain 1.1. 主要功能&#xff1a; 1.2. Langchain中提供的模块 1.3. Langchain应用场景 2.1. chatglm应用&#xff1a; 1.1. 基于单一文档问答的实现原理 chatglmlangchain GitHub - imClumsyPanda/langchain-ChatGLM: langchain-ChatGLM, local knowledge bas…