有个需求需要断网环境下上传文件,在有网环境下提交
附件需要提交到单独的文件服务器,返回文件服务器的id
框架是element-ui

设计思路

  1. 利用浏览器自带的indexDB,存储blob数据
  2. 利用localStorage缓存表单数据,indexDB的key
  3. 在有网的环境下,从indexDB获取数据,附件上传完成后,拿到id,拼接到表单的附件对象里面

封装indexDB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
if (!window.indexedDB) {
window.alert('不支持indexDB数据库');
return false;
}
const debug = true;
const log = debug ? window.console.log : () => { }

class HrIndexDB {
db = null;
dbName = null;
storeName = null;
constructor(storeName = 'storeName', dbName = "HR_INDEX_DB") {
let request = window.indexedDB.open(dbName);
this.storeName = storeName;
this.dbName = dbName;
request.onupgradeneeded = (event) => {
this.db = event.target.result;
// 不存在objectStore实例,创建实例
if (!this.db.objectStoreNames.contains(storeName)) {
const objectStore = this.db.createObjectStore(storeName, { keyPath: 'id' });
// 创建索引
objectStore.createIndex('id', 'id', { unique: false })
}
}

request.onerror = (event) => {
// 错误
}

request.onsuccess = (event) => {
this.db = event.target.result;
}
}
insert = (id, value) => {
const transcation = this.db.transcation([this.storeName], 'readwrite');
transcation.objectStore(this.storeName).add({ id, value });

transcation.onerror = (event) => {
log('insert error', event)
}
transcation.onsuccess = () => {
log('insert success')
}
}
select(id, cb) {
const objectStore = this.db.transcation([this.storeName]).objectStore(this.storeName);
const request = objectStore.get(id);

request.onsuccess = (event) => {
cb(request.result, event)
}
}
selectAsync(id) {
return new Promise((resolve, reject) => {
const objectStore = this.db.transcation([this.storeName]).objectStore(this.storeName);
const request = objectStore.get(id);

request.onsuccess = event => {
resolve(request.result, event)
}
request.onerror = (err) => {
reject(err);
console.log('indexDB selectError', err)
}
})
}
del(id) {
const objectStore = this.db.transcation([this.storeName], 'readwrite').objectStore(this.storeName, 'readwrite');
objectStore.delete(id);
}
listAll(cb) {
const objectStore = this.db.transcation([this.storeName], 'readwrite').objectStore(this.storeName, 'readwrite');
objectStore.openCursor().onsuccess = event => {
let cursor = event.target.result;
if (cursor) {
cb(cursor)
cursor.continue()
} else {

}
}
}

clearAll() {
const objectStore = this.db.transcation([this.storeName], 'readwrite').objectStore(this.storeName, 'readwrite');
objectStore.openCursor().onsuccess = event => {
let cursor = event.target.result;
if (cursor) {
cb(cursor)
this.del(cursor.key)
cursor.continue()
} else {
console.log('清空完成')
}
}
}
}
export default HrIndexDB;

拦截element-ui上传

  1. 改写httpRequest方法

    1
    2
    3
    4
    5
    <el-form>
    <el-form-item v-for="item in itemList" :key="item.key" :label="item.name" :prop="item.key">
    <el-upload :httpRequest="customHttpRequest" :data="{item,standard}" />
    </el-form-item>
    </el-form>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    const hrIndexDB = new IndexDB()
    {
    data(){
    return{
    itemList:[
    {name:"表单1",key:"formItem1",}
    ]
    }
    }
    methods:{
    customHttpRequest(option){
    const fileName = option.file.name;
    const {item,standard} = option.data.item;
    //每个表单项可以存在多个上传的附件,用于计数
    const count = item.attachmentList? item.attachmentList.length:0;
    const key = `attachment_${item.key}`;
    //将上传的文件转换成blob
    const blob = new Blob([option,file],{type:option.file.type});
    const data = {
    key,
    blob,
    fileName,
    type:option.file.type,
    inputTime
    };

    //将附件的所有数据缓存到
    hrIndexDB.insert(key,data);

    if(item.attachmentList){
    item.attachmentList.push(data)
    }else{
    item.attachmentList = [data];
    }

    }
    }
    }

存储到localStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
data(){
return{
itemList:[
{name:"表单1",key:"formItem1"}
]
}
}
methods:{
handleSave(){
this.itemList.forEach(item=>{
const key = `attachment_${item.key}`;
localStorage.setItem(key,JSON.stringify(item))
});
}
}
}

提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

{
methods:{
handleUpload(formData){
return new Promise((resolve,reject)=>{
axios({
url:'',
method:'post'
data:formData
})
})
},
// 提交
async submit(){
const taskList = [];//批量上传的表单队列
const attachmentList = [];//附件队列
this.itemList.forEach(item=>{
const data = item;
data.proofList = [];//上传完成后的
// 在拦截阶段,上传的存储到indexDB的数据
if(item.attachmentList){
item.attachmentList.forEach(attachment=>{
// 利用引用类型,把item传递过去,改变taskList里面 proofList字段
attachmentList.push({item,key:attachment.key})
})
}
})
await this.handleUploadRemoteBatch(attachmentList);

axios({
url:'',
data:taskList,
method:'post'
})

}
// 单线程上传队列
async handleUploadRemoteBatch(attachmentList){
const list = attachmentList;
let index = 0;
const Length = list.length;

while(index<Length){
const {key,item} = list[index];
const indexDBItem = await hrIndexDB.selectAsync(key);
const {value} = indexDBItem;
const {blob,fileName,type} = value;
const file = new File([blob],fileName,{type});
const forData = new FormData();
formData.append('file',file);
formData.append('fileName',fileName);

const {status,data} = await this.handleUpload(formData);
const {id} = data;
item.proofList.push({name:fileName,id});
index++
}
}
}
}
相关文章
评论
分享
  • 利用ffmpeg转码B站缓存

    由于工作原因驻场开发,地区限制,酒店竟没有wifi,所以开了流量套餐 其中就包括B站在线观看,缓存视频单独走流量计算,每个月有30G的流量, 虽然可以愉快的手机刷B站,但是明显PC看视频才更爽, 且开热点给PC看B站的流量是不算在...

    利用ffmpeg转码B站缓存
  • 服务器挂机下载百度网盘

    非业务需求,只是觉得开着电脑挂机下载百度网盘的内容实在是太慢了突发奇想,能不能用服务器挂机下载,反正开着也是开着上网搜索一番,还真的有现成的实现 有人利用百度网盘开源的api,写了个下载上传同步的工具(https://github.c...

    服务器挂机下载百度网盘
  • serveless踩坑-同步机制

    没有真正的异步 12345678910router.get('/track', async (req, res) => { track( ...req.query ); res.type('image/png')....

    serveless踩坑-同步机制
  • serveless踩坑-图片上传

    图片上传简单的图片上传前端1<input type="file" id="file"> 1234567891011121314function upload() { var formData = ne...

    serveless踩坑-图片上传
  • react-native开发指南

    React Native 中设置 APP 名称、图标和启动页https://www.jianshu.com/p/727c6057fc0a 设置项目名android/app/src/main/res/values/strings.xml...

    react-native开发指南
  • 纯css添加div

    今天接到一个需求,在贴片图片的左下角添加一段文字,表明“广告”二字乍一听挺简单的,后来拿到源码发现不对劲 1234<script type='text/javascript' charset='utf-8' src='http:...

    纯css添加div
  • 82.删除排序链表中的重复元素II

    82. 删除排序链表中的重复元素 II给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。示例1输入: 1->2->3->3->4->4->5输出: 1->2...

    82.删除排序链表中的重复元素II
  • hexo使用手册

    My Name is NNNNzs 文章Front-matter是文件最上方以 — 分隔的区域,用于指定个别文件的变量,举例来说: 12345678---title: hexo使用手册date: 2019-08-10 00:25:19...

    hexo使用手册
  • 微任务宏任务

    微任务队列宏任务队列 javascript是单线程,多线程都是模拟出来的, script、setTimeOut、setInterval是宏任务 Promise,process.nextTick是微任务 setTimeOut是n毫...

    微任务宏任务
  • 粘贴图片上传功能

    粘贴图片并上传最近在写博客的时候,纯MarkDown语法写,但是发现图片是个需求,需要经常截图并且写到MarkDown里面之前的做法是用QQ截图,然后另存为到本地桌面,然后再上传,返回URI地址,贴到博客编辑器里面最近发现有些页面支持...

    粘贴图片上传功能