获取中...

-

Just a minute...

有个需求需要断网环境下上传文件,在有网环境下提交
附件需要提交到单独的文件服务器,返回文件服务器的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
18
{
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
61

{
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++
}
}
}
}

相关文章
评论
分享
  • 利用微信小程序扫码授权

    微信小程序扫码授权背景想要使用微信扫码登录自己的网址,通过授权快速获取用户的昵称,头像功能由于没有企业认证账号,故只能通过微信小程序实现, 体验地址https://api.nnnnzs.cn/screen-demo.html?env=...

    利用微信小程序扫码授权
  • 强制加载element-dialog

    强制加载element-dialog背景123<el-dialog> <MyComponent /></el-dialog> 自己封装的组件 MyComponent ,放在了el-dialog里...

    强制加载element-dialog
  • leancloud-基础存储操作

    对象安装使用1234npm install leancloud-storage --save# debug模式DEBUG=leancloud* node src/leancloud.js 初始化12345678const AV = ...

    leancloud-基础存储操作
  • leetcode-540-有序数组中的单一元素

    给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。请你找出并返回只出现一次的那个数。你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。 示例 1:输入: nums = [1...

    leetcode-540-有序数组中的单一元素
  • 1995.统计特殊四元组

    1995. 统计特殊四元组给你一个 下标从 0 开始 的整数数组 nums ,返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 :nums[a] + nums[b] + nums[c] == nums[d] ,且a...

    1995.统计特殊四元组
  • leetcode-1078.Bigram 分词

    给出第一个词 first 和第二个词 second,考虑在某些文本 text 中可能以 “first second third” 形式出现的情况,其中 second 紧随 first 出现,third 紧随 second 出现。对于每...

    leetcode-1078.Bigram 分词
  • leetcode-1705.吃苹果的最大数目

    有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,树上会长出 apples[i] 个苹果,这些苹果将会在 days[i] 天后(也就是说,第 i + days[i] 天时)腐烂,变得无法食用。也可能有那么几天,...

    leetcode-1705.吃苹果的最大数目
  • leetcode-1154.一年中的第几天

    1154. 一年中的第几天给你一个字符串 date ,按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。请你计算并返回该日期是当年的第几天。通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 ...

    leetcode-1154.一年中的第几天
  • leetcode-475.供暖器

    冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。在加热器的加热半径范围内的每个房屋都可以获得供暖。现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋...

    leetcode-475.供暖器
  • leetcode-997.找到小镇的法官

    997. 找到小镇的法官在一个小镇里,按从 1 到 n 为 n 个人进行编号。传言称,这些人中有一个是小镇上的秘密法官。如果小镇的法官真的存在,那么:小镇的法官不相信任何人。每个人(除了小镇法官外)都信任小镇的法官。只有一个人同时满足...

    leetcode-997.找到小镇的法官