IndexedDB实现简单的文件管理器
使用indexedDB数据库作为数据存储实现的一个网页文件管理器
上界面
上代码
<template>
<div>
<button @click="init">初始化</button>
<button @click="add">添加</button>
<button @click="get">获取</button>
<button @click="del">删除</button>
<button @click="clear">清空</button>
<button @click="index">获取一条索引记录</button>
<button @click="indexAll">获取一定数量索引记录</button>
</div>
<div class="path">
<button @click="handleRefresh">刷新</button>
<button @click="handleUpper">返回上一层</button>
路径:<input v-model="path" placeholder="文件路径" @keyup.enter="handleRefresh"/>
新建文件夹:<input v-model="newDir" placeholder="新建文件夹"/><button @click="handleMkdir">新建</button>
新建文件:<input v-model="newFile" placeholder="新建文件"/><button @click="handleMkfile">新建</button>
<button @click="handleUpload">上传文件</button>
<button @click="handlePaste">粘贴</button>
</div>
<div class="file-list">
<div class="file" :class="{active:file.id === active}" v-for="(file,index) in fileList" :key="'file' + index" @click="handleClick(file)" @dblclick="handleDbClick(file)">
<div class="name">{
{ file.name }}</div>
<div class="type">{
{ file.type === fileTypeEnum.FILE?'文件':'文件夹' }}</div>
<div class="created-date">{
{ file.craterDate }}</div>
<div class="action">
<span @click.stop="rename(file)">重命名</span>|
<span @click.stop="move(file)">剪切</span>|
<span @click.stop="copy(file)">复制</span>|
<span @click.stop="remove(file)">删除</span>|
<span v-if="file.type === fileTypeEnum.FILE" @click="download(file)">下载</span>
</div>
</div>
</div>
<KeepAlive>
<file-info :file="file"/>
</KeepAlive>
</template>
<script setup>
import dayjs from 'dayjs'
import { ref , toRaw } from 'vue'
import FileInfo from './components/file-info.vue'
const fileTypeEnum = {
FILE:'file',
DIR:'dir'
}
const path = ref('/')
const newDir = ref('')
const file = ref({})
const handleMkdir = () => {
console.log("新建文件夹",newDir.value)
const file = {
name: newDir.value ,
data: '' ,
craterDate: dayjs().format('YYYY-MM-DD HH:mm:ss') ,
path: path.value,
type: fileTypeEnum.DIR
}
console.log('文件',file)
add(file)
setTimeout(() => {
handleRefresh()
},100)
}
const newFile = ref('')
const handleMkfile = () => {
console.log("新建文件",newFile.value)
const file = {
name: newFile.value ,
data: '' ,
craterDate: dayjs().format('YYYY-MM-DD HH:mm:ss') ,
path: path.value,
type: fileTypeEnum.FILE
}
add(file)
setTimeout(() => {
handleRefresh()
},100)
}
const fileList = ref([])
const active = ref('')
const handleClick = (file) => {
console.log("单击",file)
active.value = file.id
}
const handleDbClick = (f) => {
console.log("双击",f)
if(f.type === fileTypeEnum.DIR){
path.value = path.value + f.name + '/'
setTimeout(() => {
handleRefresh()
},100)
}
if(f.type === fileTypeEnum.FILE){
const fr = new FileReader()
fr.readAsDataURL(new Blob([f.blob]));
fr.onloadend = function(){
console.log("读取结果",fr.result)
f.url = fr.result
file.value = f
}
// window['fileUrl'] = URL.createObjectURL(new Blob([f.blob]))
// file.value = f
// fileUrl.value = window.fileUrl
// console.log('window.fileUrl',window.fileUrl)
// blobToDataURI(new Blob([f.data]),(base64Url) => {
// console.log("生产base64成功")
// file.value = f
// file.value.url = base64Url
// })
}
}
function blobToDataURI(blob, callback) {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = function (e) {
callback(e.target.result);
}
}
const handleUpper = () => {
console.log("返回上一层",path.value.split('/'))
const pathArr = path.value.split('/')
if(path.value === '/') {
console.log("根目录")
return
}
pathArr.splice(pathArr.length-2,1)
console.log(pathArr.length,pathArr)
path.value = pathArr.join('/')
setTimeout(() => {
handleRefresh()
},100)
}
const handleRefresh = () => {
console.log("刷新")
if(path.value[path.value.length-1] !== '/') {
path.value += '/'
}
indexAll(path.value)
}
const handleUpload = () => {
console.log("上传文件")
let input = document.createElement('input');
input.type = 'file';
input.onchange = e => {
const file = e.target.files[0];
const fileBlob = URL.createObjectURL(file);
console.log('file',file);
console.log('fileBlob',fileBlob);
saveFile(fileBlob).then(res => {
console.log("文件",res)
const uploadFile = {
name: file.name ,
size: file.size ,
extType: file.type ,
data: res ,
craterDate: dayjs().format('YYYY-MM-DD HH:mm:ss') ,
path: path.value,
type: fileTypeEnum.FILE
}
console.log("添加的文件",uploadFile)
add(uploadFile)
setTimeout(() => {
handleRefresh()
},100)
})
}
input.click();
}
const saveFile = (url) => {
console.log("保存文件")
return new Promise((resolve) => {
let xhr = new XMLHttpRequest()
xhr.open( "GET" , url, true );
xhr.responseType = "blob"
xhr.addEventListener( "load" , function () {
if (xhr.status === 200) {
console.log("文件blob",xhr.response)
resolve(xhr.response)
}
}, false );
xhr.send();
})
}
const getFile = (blob) => {
return URL.createObjectURL(new Blob([blob]))
}
const download = (file) => {
console.log("下载",file)
saveAs(file.data,file.name)
}
const remove = (file) => {
console.log('删除',file)
const conf=confirm('你确认删除' + file.name + '吗?');
if(conf){
del(file.id)
setTimeout(() => {
handleRefresh()
},100)
}
}
const rename = (file) => {
console.log("重命名",file)
const name=prompt("请输入新文件名 :",file.name);
console.log("新文件名",name)
file.name = name
update(file)
setTimeout(() => {
handleRefresh()
},100)
}
let actionType = ''
let actionOrigin = ''
const copy = (file) => {
console.log("复制",file)
actionType = 'copy'
actionOrigin = file
}
const move = (file) => {
console.log("复制",file)
actionType = 'move'
actionOrigin = file
}
const handlePaste = () => {
console.log("粘贴",actionType,actionOrigin,path)
if(path.value === actionOrigin.path) {
console.log("原始目录不能粘贴")
return
}
const file = toRaw(actionOrigin)
file.path = path.value
switch (actionType ) {
case 'copy':
console.log("复制")
add(file)
break;
case 'move':
console.log("移动")
update(file)
break;
}
setTimeout(() => {
handleRefresh()
},100)
}
function saveAs(blob, filename) {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename)
} else {
const link = document.createElement('a')
const body = document.querySelector('body')
link.href = window.URL.createObjectURL(blob)
console.log('创建的文件名', filename)
link.download = filename
link.style.display = 'none'
body.appendChild(link)
link.click()
body.removeChild(link)
window.URL.revokeObjectURL(link.href)
}
}
// 数据库名
let dbName = "file-system"
// 数据库版本
let dbVersion = 1
// 集合名
let storeName = 'files'
// 数据库对象
let db = ''
const init = () => {
console.log("初始化")
if (!window.indexedDB) {
window.alert('浏览器不支持IndexDB!')
return
}
let request = indexedDB.open(dbName, dbVersion)
//打开数据库失败
request.onerror = function(event) {
console.log(event.target.errorCode)
}
//打开数据库成功
request.onsuccess = function(event) {
console.log("打开数据库成功",event)
db = event.target.result
}
// 数据库升级
request.onupgradeneeded = function(event) {
db = event.target.result
// 创建数据集合
const store = db.createObjectStore(storeName,{ autoIncrement: true , keyPath: "id" })
// 创建索引
store.createIndex("id", "id", { unique: false });
store.createIndex("name", "name", { unique: false });
store.createIndex("path", "path", { unique: false });
}
}
const add = (file) => {
console.log("添加")
delete file.id
// 打开事务
let transaction = db.transaction(storeName, 'readwrite')
// 搜索数据库表
let store = transaction.objectStore(storeName)
// 操作
let request = store.put(file)
request.onsuccess = function() {
console.log('添加成功')
}
request.onerror = function(event) {
console.log('添加失败',event)
}
}
const get = () => {
console.log("查询")
// 获取事务
let transaction = db.transaction(storeName, 'readwrite')
// 获取集合
let store = transaction.objectStore(storeName)
// 获取id=2
let request = store.get(2) // store.getAll() //key ? store.get(key) : store.getAll()
request.onsuccess = function () {
console.log(request.result)
}
}
const update = (file) => {
console.log("修改",file.id)
if(!file.id) return
// 打开事务
let transaction = db.transaction(storeName, 'readwrite')
// 搜索数据库表
let store = transaction.objectStore(storeName)
// 操作 必须为原生对象
const data = toRaw(file)
let request = store.put(data)
request.onsuccess = function() {
console.log('添加成功')
}
request.onerror = function(event) {
console.log('添加失败',event)
}
}
const del = (id) => {
console.log("删除")
let transaction = db.transaction(storeName, 'readwrite')
let store = transaction.objectStore(storeName)
let request = store.delete(id)
request.onsuccess = function() {
console.log('删除成功')
}
}
const clear = () => {
console.log("清楚")
let transaction = db.transaction(storeName, 'readwrite')
// 清除整个集合
let store = transaction.objectStore(storeName)
let request = store.clear()
request.onsuccess = function(){
console.log('清除成功')
}
}
const index = () => {
console.log("索引")
let transaction = db.transaction(storeName, 'readwrite')
let store = transaction.objectStore(storeName)
let index = store.index('name');
// 查询一个符合索引的
const request = index.get('1.txt')
request.onsuccess = function () {
console.log(request.result)
}
}
const indexAll = (path) => {
console.log("索引")
let transaction = db.transaction(storeName, 'readwrite')
let store = transaction.objectStore(storeName)
let index = store.index('path');
// 查询符合索引的 X个 不填为全部
// const request = index.getAll('1.txt',55)
const request = index.getAll(path)
request.onsuccess = function () {
console.log(request.result)
fileList.value = request.result
}
}
init()
setTimeout(() => {
handleRefresh()
},100)
</script>
<style lang="less" scoped>
.file-list{
height: 500px;
overflow: auto;
.file{
display: flex;
&:hover{
background: #eee;
transition: 0.2s;
}
&.active{
background: #ddd;
}
.name{
flex: 1;
}
.type{
width: 80px;
}
.created-date{
width: 150px;
}
.action{
width: 200px;
>span{
cursor: pointer;
}
}
}
}
</style>
<template>
<div class="file-info">
{
{ file.name }}
<img v-if="file['extType'] && file['extType'].indexOf('image') !== -1" :src="getFile(file.data)" alt=""/>
<video :key="file.id" v-if="file['extType'] && file['extType'].indexOf('video') !== -1" :src="getFile(file.data)" autoplay :alt="file.name" width="320" height="240" controls>
<source :type="file['extType']">
</video>
</div>
</template>
<script>
export default {
name: "file-info",
props:{
file:Object,
default: () => {}
},
methods:{
getFile (blob) {
return URL.createObjectURL(new Blob([blob]))
}
}
}
</script>
<style lang="less" scoped>
.file-info{
height: 300px;
img{
max-height: 100%;
}
}
</style>