MEMFS实现简单的文件管理器
使用的是ffmpeg里的fs模块
fs的api可以参考emscripten的文档
上界面
上代码
<template>
<div>
fs文件系统
<button @click="readDir">读取文件夹</button>
<input @change="changFile" type="file" id="uploader" placeholder="文件">
<button @click="saveFile">保存</button>
<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>
</div>
<div class="file-list">
<div class="file" :class="{active:file.name === active}" v-for="(file,index) in fileList" :key="'file' + index" @click="handleClick(file)" @dblclick="handleDbClick(file)">
<div class="preview">
</div>
<div class="name">{
{ file.name }}</div>
<div class="size">{
{ file.size }}</div>
<div class="type">{
{ file.type === fileTypeEnum.FILE?'文件':'文件夹' }}</div>
<div class="created-date">{
{ file.createDate }}</div>
<div class="action">
<span @click.stop="rename(file)">重命名</span>|
<span @click.stop="remove(file)">删除</span>|
<span v-if="file.type === fileTypeEnum.FILE" @click="download(file)">下载</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
// 本身项目用了ffmpeg,所以这里使用的是ffmpeg里的FS模块
import { createFFmpeg , fetchFile } from '@ffmpeg/ffmpeg'
import dayjs from 'dayjs'
import { onMounted , ref } from 'vue'
let fs = ''
const fileTypeEnum = {
FILE:'file',
DIR:'dir'
}
const path = ref('/')
const newDir = ref('')
const file = ref({})
const newFile = ref('')
const fileList = ref([])
const active = ref('')
onMounted(async () => {
let ffmpeg = createFFmpeg( {
log: true
})
await ffmpeg.load();
fs = ffmpeg.FS
console.log("fs",fs)
handleRefresh()
})
let tmpFile = ''
const changFile = (file) => {
console.log("选择文件",file.target.files)
tmpFile = file.target.files[0]
}
const saveFile = async () => {
console.log("保存文件",tmpFile)
fs( 'writeFile' , tmpFile.name , await fetchFile(tmpFile) );
const data = fs( 'readFile' , tmpFile.name );
console.log("文件数据",data)
handleRefresh()
}
const readDir = (path) => {
const list=fs('readdir',path)
console.log("文件列表",list)
// fileList.value = list
const showList = []
list.forEach(file => {
if(file !== '.' && file !== '..' ){
const item = {
name:file,
type:'',
size:"",
createDate:''
}
// 获取文件属性
const info=fs('stat',path + file)
console.log("info",info)
item.createDate = dayjs(info.ctime).format('YYYY-MM-DD HH:mm:ss')
item.size = info.size
// 判断文件类型
const isFile=fs('isFile',info.mode + '')
item.type = isFile?fileTypeEnum.FILE:fileTypeEnum.DIR
showList.push(item)
}
})
fileList.value = showList
}
const handleRefresh = () => {
console.log("刷新")
if(path.value[path.value.length-1] !== '/') {
path.value += '/'
}
readDir(path.value)
}
const handleUpper = () => {
console.log("返回上一层")
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 handleMkdir = () => {
console.log("新建文件夹",newDir.value)
const data=fs('mkdir',path.value + newDir.value)
console.log("结果",data)
handleRefresh()
}
const handleMkfile = () => {
console.log("新建文件")
const data=fs('writeFile',path.value+newFile.value,'')
console.log("结果",data)
handleRefresh()
}
const handleUpload = () => {
console.log("上传文件")
let input = document.createElement('input');
input.type = 'file';
input.onchange = e => {
tmpFile = e.target.files[0];
console.log("添加的文件",tmpFile)
saveFile()
setTimeout(() => {
handleRefresh()
},100)
}
input.click();
}
const handleClick = (file) => {
console.log("单机",file)
active.value = file.name
}
const handleDbClick = (file) => {
console.log("双击",file)
if(file.type === fileTypeEnum.DIR) {
active.value = ''
path.value = path.value + file.name + '/'
setTimeout(() => {
handleRefresh()
},100)
}
if(file.type === fileTypeEnum.FILE) {
const data = fs( 'readFile' ,path.value + file );
console.log("文件数据",data)
}
}
const download = (file) => {
console.log("下载",file)
const data = fs( 'readFile' ,path.value + file.name );
const blob = new Blob([data])
console.log("文件数据",data)
saveAs(blob,file.name)
}
const rename = (file) => {
console.log("重命名",file)
const name=prompt("请输入新文件名 :",file.name);
console.log("新文件名",name)
const data = fs( 'rename' ,path.value + file.name,path.value + name );
console.log("重命名",data)
setTimeout(() => {
handleRefresh()
},100)
}
const remove = (file) => {
console.log("删除",file)
if(file.type === fileTypeEnum.DIR) {
const data = fs( 'rmdir' ,path.value + file.name );
console.log("删除",data)
}
if(file.type === fileTypeEnum.FILE) {
const data = fs( 'unlink' ,path.value + file.name );
console.log("删除",data)
}
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)
}
}
</script>
<style lang="less" scoped>
.file-list{
height: 500px;
overflow: auto;
.file{
display: flex;
&:hover{
background: #eee;
transition: 0.2s;
}
&.active{
background: #ddd;
}
.preview{
width: 50px;
text-align: center;
}
.name{
flex: 1;
}
.size{
width: 120px;
}
.type{
width: 80px;
}
.created-date{
width: 150px;
}
.action{
width: 200px;
>span{
cursor: pointer;
}
}
}
}
</style>