【前端】之IndexedDB实现简单的文件管理器

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>

猜你喜欢

转载自blog.csdn.net/sky529063865/article/details/128315527