import axios from 'axios'
import get from 'lodash-es/get.js'
import isWindow from 'wsemi/src/isWindow.mjs'
import evem from 'wsemi/src/evem.mjs'
import genPm from 'wsemi/src/genPm.mjs'
import haskey from 'wsemi/src/haskey.mjs'
import isfun from 'wsemi/src/isfun.mjs'
import ispint from 'wsemi/src/ispint.mjs'
import isp0int from 'wsemi/src/isp0int.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import isobj from 'wsemi/src/isobj.mjs'
import iseobj from 'wsemi/src/iseobj.mjs'
import ispm from 'wsemi/src/ispm.mjs'
import cint from 'wsemi/src/cint.mjs'
import strright from 'wsemi/src/strright.mjs'
import b642str from 'wsemi/src/b642str.mjs'
import blob2u8arr from 'wsemi/src/blob2u8arr.mjs'
import obj2u8arr from 'wsemi/src/obj2u8arr.mjs'
import u8arr2obj from 'wsemi/src/u8arr2obj.mjs'
import pmConvertResolve from 'wsemi/src/pmConvertResolve.mjs'
import getFileXxHash from 'wsemi/src/getFileXxHash.mjs'
/**
* 建立Hapi使用者(Node.js與Browser)端物件
*
* @class
* @param {Object} opt 輸入設定參數物件
* @param {String} [opt.url='http://localhost:8080'] 輸入Hapi伺服器網址,預設為'http://localhost:8080'
* @param {String} [opt.apiName='api'] 輸入API名稱字串,預設'api'
* @param {Function} [opt.getToken=()=>''] 輸入取得使用者token的回調函數,預設()=>''
* @param {String} [opt.tokenType='Bearer'] 輸入token類型字串,預設'Bearer'
* @param {Integer} [opt.sizeSlice=1024*1024] 輸入切片上傳檔案之切片檔案大小整數,單位為Byte,預設為1024*1024
* @param {Integer} [opt.retry=3] 輸入傳輸失敗重試次數整數,預設為3
* @returns {Object} 回傳事件物件,可使用函數execute、upload
* @example
*
* import path from 'path'
* import fs from 'fs'
* import _ from 'lodash-es'
* import w from 'wsemi'
* import FormData from 'form-data'
* import WConverhpClient from './src/WConverhpClient.mjs'
*
* let ms = []
*
* let opt = {
* FormData,
* url: 'http://localhost:8080',
* apiName: 'api',
* getToken: () => {
* return 'token-for-test'
* },
* }
*
* //new
* let wo = new WConverhpClient(opt)
*
* wo.on('error', (err) => {
* console.log(`error`, err)
* })
*
* function downloadLargeFile() {
* let core = async() => {
*
* await wo.download('id-for-file',
* function ({ prog, p, m }) {
* // console.log('client web: download: prog', prog, p, m)
* if (m === 'download') {
* console.log('client web: download: prog', prog)
* }
* },
* {
* fdDownload: './', //於nodejs環境才能提供
* })
* .then(function(res) {
* console.log('client web: download: then', res)
* ms.push({ 'download output': res })
* })
* .catch(function (err) {
* console.log('client web: download: catch', err)
* })
*
* console.log('ms', ms)
*
* }
* core()
* }
*
* downloadLargeFile()
*
*/
function WConverhpClient(opt) {
//_url
let _url = get(opt, 'url')
if (!isestr(_url)) {
_url = 'http://localhost:8080'
}
//apiName
let apiName = get(opt, 'apiName')
if (!isestr(apiName)) {
apiName = 'api'
}
//url
let url = ''
if (strright(_url, 1) === '/') {
url = _url + apiName
}
else {
url = _url + '/' + apiName
}
//getToken
let getToken = get(opt, 'getToken', null)
if (!isfun(getToken)) {
getToken = () => {
return ''
}
}
//tokenType
let tokenType = get(opt, 'tokenType')
if (!isestr(tokenType)) {
tokenType = 'Bearer'
}
//sizeSlice
let sizeSlice = get(opt, 'sizeSlice')
if (!ispint(sizeSlice)) {
sizeSlice = 1024 * 1024 //1m
}
//retry
let retry = get(opt, 'retry')
if (!isp0int(retry)) {
retry = 3
}
//env
let env = isWindow() ? 'browser' : 'nodejs'
// console.log('env', env)
//ee
let ee = evem() //new events.EventEmitter()
//eeEmit
let eeEmit = (name, ...args) => {
setTimeout(() => {
ee.emit(name, ...args)
}, 1)
}
//getUrlUse
let getUrlUse = (type) => {
//urlUse
let urlUse = ''
if (type === 'basic') {
urlUse = url
}
else if (type === 'slice') {
urlUse = `${url}slc`
}
else if (type === 'upload-controller') {
urlUse = `${url}ulctr`
}
// else if (type === 'slice-merge') {
// urlUse = `${url}slcm`
// }
else if (type === 'download-get-filename') {
urlUse = `${url}dwgfn`
}
else if (type === 'download-get') {
urlUse = `${url}dwgf`
}
else if (type === 'download') {
urlUse = `${url}dw`
}
else {
throw new Error(`invalid type[${type}]`)
}
return urlUse
}
//res2u8arr
async function res2u8arr(bb) {
//blob(in browser) or buffer(in nodejs) to u8a
let u8a
if (env === 'browser') {
u8a = await blob2u8arr(bb)
}
else {
u8a = new Uint8Array(bb)
}
return u8a
}
//u8arr2bb
function u8arr2bb(u8a) {
//u8a to blob(in browser) or buffer(in nodejs)
let bb
if (env === 'browser') {
bb = new Blob([u8a.buffer])
}
else { //nodejs
bb = Buffer.from(u8a)
}
return bb
}
//send
async function send(type, pkg, opt = {}) {
//headers
let headers = get(opt, 'headers')
if (!isobj(headers)) {
headers = {}
}
// console.log('headers', headers)
//dataType
let dataType = get(opt, 'dataType', '')
if (dataType !== 'blob' && dataType !== 'fmd' && dataType !== 'json') {
dataType = 'blob'
}
// console.log('dataType', dataType)
//cbProgress
let cbProgress = get(opt, 'cbProgress')
if (!isfun(cbProgress)) {
cbProgress = () => {}
}
//urlUse
let urlUse = getUrlUse(type)
//pm
let pm = genPm()
//dd, ct
let dd = null
let ct = {}
if (dataType === 'blob') {
//set ct
ct = {
'Content-Type': 'application/octet-stream',
}
//set dd
dd = pkg
}
else if (dataType === 'fmd') {
//fmd
let fmd
if (env === 'browser') {
fmd = new FormData()
}
else {
if (isfun(opt.FormData)) {
fmd = new opt.FormData({ maxDataSize: 1024 * 1024 * 1024 * 1024 }) //nodejs, 使用套件form-data設定資料量最大為1tb
}
else {
// console.log(`invalid opt.FormData, need [npm i form-data] and [import FormData from 'form-data'] to set opt.FormData = FormData`)
eeEmit('error', `invalid opt.FormData, need [npm i form-data] and [import FormData from 'form-data'] to set opt.FormData = FormData`)
throw new Error('invalid opt.FormData')
}
}
//append
fmd.append('bb', pkg)
// console.log('fmd', fmd)
//set ct
if (env === 'nodejs') {
ct = {
'Content-Type': `multipart/form-data; boundary=${fmd.getBoundary()}` //nodejs, 使用套件form-data需設定boundary
}
// console.log('ct', ct)
}
else {
//browser會自動根據fmd的邊界boundary來設定
}
//set dd
dd = fmd
}
else if (dataType === 'json') {
//axios預設會將物件自動轉換為JSON, 此處指定Content-Type且強制轉, 避免可能問題
//set ct
ct = {
'Content-Type': 'application/json',
}
dd = JSON.stringify(pkg)
}
// console.log('dd', dd)
//rt
let rt = 'blob'
if (env === 'nodejs') {
rt = 'arraybuffer' //nodejs下沒有blob, 只能設定'json', 'arraybuffer', 'document', 'json', 'text', 'stream'
if (type === 'download') {
rt = 'stream' //nodejs download模式採用stream接收
}
}
// console.log('rt', rt)
//token
let token = getToken()
if (ispm(token)) {
token = await token
}
//s
let s = {
method: 'POST',
url: urlUse,
data: dd,
headers: {
Authorization: `${tokenType} ${token}`,
...ct,
...headers,
},
timeout: 60 * 60 * 1000, //1hr
maxContentLength: Infinity, //1024 * 1024 * 1024, Infinity //axios於nodejs中會限制內容大小故需改為無限
maxBodyLength: Infinity, //1024 * 1024 * 1024, Infinity //axios於nodejs中會限制內容大小故需改為無限
responseType: rt,
onUploadProgress: function(ev) {
//console.log('onUploadProgress', ev)
//r
let r = 0
let loaded = ev.loaded
let total = ev.total
if (ispint(total)) {
r = (loaded * 100) / total
}
//cbProgress
cbProgress({ prog: Math.floor(r), p: loaded, m: 'upload' })
},
onDownloadProgress: function (ev) {
// console.log('onDownloadProgress', ev)
//r
let r = 0
let loaded = ev.loaded
// let total = ev.srcElement.getResponseHeader('Content-length') //若需要得知下載進度, 需於伺服器回傳時提供Content-length
let total = ev.total
if (ispint(total)) {
r = (loaded * 100) / total
}
//cbProgress
cbProgress({ prog: Math.floor(r), p: loaded, m: 'download' })
},
}
// console.log('s', s)
//getFilenameByHeader
let getFilenameByHeader = (contentDisposition) => {
let fn = 'unknow'
try {
let reg = /filename="(.+?)"/
let matches = reg.exec(contentDisposition)
fn = matches ? matches[1] : 'unknown'
}
catch (err) {}
return fn
}
//downloadStream
let downloadStream = async (res) => {
// console.log('res.headers', res.headers)
//pm
let pm = genPm()
//returnType
let returnType = get(res, `headers['return-type']`, '')
// console.log('returnType', returnType)
//returnMsg
let returnMsg = get(res, `headers['return-msg']`, '')
// console.log('returnMsg', returnMsg)
//check
if (returnType === 'error') {
pm.reject(returnMsg)
return pm
}
//contentDisposition
let contentDisposition = get(res, `headers['content-disposition']`, '')
// console.log('contentDisposition', contentDisposition)
//filename
let filename = getFilenameByHeader(contentDisposition)
filename = b642str(filename) //headers內對中文支援度不佳須用base64傳, 此處解析提取後須反轉
// console.log('filename', filename)
//streamRecv
let streamRecv = get(res, 'data')
if (env === 'browser') {
//browser通過createObjectURL接收stream與a.href+a.click()接收檔案
try {
let url = URL.createObjectURL(streamRecv)
let a = document.createElement('a')
a.href = url
a.download = filename // 動態設置檔名
document.body.appendChild(a)
a.click()
a.remove()
URL.revokeObjectURL(url)
pm.resolve(filename)
}
catch (err) {
console.log(err)
pm.reject(err)
}
}
else {
//nodejs通過fs與stream接收檔案, stream出錯只會觸發error事件, 此處try catch為攔截其他非stream程式碼錯誤
try {
//path, fs, 使用動態import供nodejs使用, 否則會被webpack偵測報錯
let path = await import('path')
let fs = await import('fs')
//fdDownload, 只有nodejs下載才使用fdDownload
let fdDownload = get(opt, 'fdDownload', '')
fs.mkdirSync(fdDownload, { recursive: true }) //須使用mkdirSync, 不要用fsIsFolder與fsCreateFolder避免編譯
// console.log('fdDownload', fdDownload)
//fp
let fp = path.resolve(fdDownload, filename)
// console.log('fp', fp)
//streamWriter
let streamWriter = fs.createWriteStream(fp)
//pipe
streamRecv.pipe(streamWriter)
//finish
streamWriter.on('finish', () => {
pm.resolve(fp)
})
//error
streamWriter.on('error', (err) => {
pm.reject(err)
})
}
catch (err) {
pm.reject(err)
}
}
return pm
}
//axios
axios(s)
.then(async (res) => {
// console.log('axios then', res)
//check download
if (type === 'download') {
await downloadStream(res)
.then((_res) => {
pm.resolve(_res)
})
.catch((_err) => {
pm.reject(_err)
})
return
}
//bb
let bb = get(res, 'data')
// console.log('bb', bb)
//res2u8arr
let u8a = await res2u8arr(bb)
// console.log('u8a', u8a)
//u8arr2obj
let data = u8arr2obj(u8a)
// console.log('data', data)
//check
if (!iseobj(data)) {
// console.log('data is not an effective object', data)
eeEmit('error', `data is not an effective object`)
pm.reject(`data is not an effective object`)
return
}
//分離伺服器資料的success或error
if (haskey(data, 'success')) {
pm.resolve(data.success)
}
else if (haskey(data, 'error')) {
eeEmit('error', data.error)
pm.reject(data.error)
}
else {
// console.log('invalid data', data)
eeEmit('error', `invalid data`)
pm.reject(`invalid data`)
}
})
.catch(async (res) => {
// console.log('axios catch', res.toJSON())
//Network Error除可能是網路斷線之外, 可能被瀏覽器外掛封鎖阻擋, 亦可能因硬碟空間不足(<4g)無法下載被瀏覽器拒絕
//data
let data = null
//statusText, err
let statusText = get(res, 'response.statusText') || get(res, 'message')
let err = get(res, 'response.data') || get(res, 'stack')
// console.log(`get(res, 'response.statusText')`, get(res, 'response.statusText'))
// console.log(`get(res, 'message')`, get(res, 'message'))
// console.log(`get(res, 'response.data')`, get(res, 'response.data'))
// console.log(`get(res, 'stack')`, get(res, 'stack'))
if (statusText) {
// console.log('statusText', statusText)
data = statusText
}
else if (err) {
// console.log('err', err)
data = err
}
else {
try {
res = res.toJSON()
}
catch (err) {}
// console.log('err', res)
eeEmit('error', res)
data = 'Can not connect to server.'
}
if (data === 'Network Error') {
data = `Network Error. Make sure your space of hard drive is large enough or blocking by browser plugins.`
}
// console.log('data', data)
pm.reject(data)
})
return pm
}
//sendPkg
async function sendPkg(type, data, cbProgress) {
//bb
let bb = null
try {
//obj2u8arr
let u8a = obj2u8arr(data)
// console.log('u8a', u8a)
//u8a to blob(in browser) or buffer(in nodejs)
bb = u8arr2bb(u8a)
// console.log('bb', bb)
}
catch (err) {
return Promise.reject(err)
}
//send
let res = await send(type, bb, { dataType: 'blob', cbProgress })
return res
}
//sendData
async function sendData(type, data, cbProgress) {
//fun
let fun = pmConvertResolve(sendPkg)
//sendPkg
let r = await fun(type, data, cbProgress)
let n = 0
while (r.state === 'error') {
n += 1
if (n > retry) {
break
}
console.log(`retry n=${n}`)
r = await fun(type, data, cbProgress)
}
if (r.state === 'success') {
return r.msg
}
else {
return Promise.reject(r.msg)
}
}
//calcHash
async function calcHash(inp) {
//bb
let bb = null
if (env === 'browser') {
bb = inp
}
else {
//於nodejs時, 因尚無法提供檔名上傳, 故會是readFileSync讀入的buffer, 再轉成new Blob([buffer]), 供getFileXxHash使用
bb = new Blob([inp])
}
//hash
let hash = await getFileXxHash(bb)
return hash
}
//sendDataSlice
async function sendDataSlice(fileTotalName, bb, cbProgress) {
//n
let n = 0
if (n === 0) {
try {
n = bb.size //for Blob
n = cint(n)
}
catch (err) {}
}
if (n === 0) {
try {
n = bb.length //for ArrayBuffer //nodejs用fs讀有檔案大小上限, 除非改傳入檔名用stream讀, 否則無法支援超大檔
n = cint(n)
}
catch (err) {}
}
if (n === 0) {
// eeEmit('error', `can not get size of bb`)
// return Promise.reject(`can not get size of bb`)
n = 1 //最小給1, 使能支援無大小檔案上傳
}
// console.log('n', n)
//fileTotalSize
let fileTotalSize = n
//chunkTotal
let chunkTotal = Math.ceil(fileTotalSize / sizeSlice)
// console.log('chunkTotal', chunkTotal)
//progCount, progWeightSlice
let progCount = 0
let progWeightSlice = 0.99 //上傳階段進度使用99%
// let progWeightMerge = 0.01 //合併階段進度使用1%
//cbProgressSlice
let cbProgressSlice = (msg) => {
let perc = msg.prog
let dir = msg.m
if (dir === 'upload' && perc === 100) {
progCount++
let r = progCount / chunkTotal
let prog = r * progWeightSlice * 100
let psiz = r * fileTotalSize
cbProgress({ prog, p: psiz, m: 'upload' })
}
}
//cbProgressMerge
let cbProgressMerge = (msg) => {
let perc = msg.prog
let dir = msg.m
if (dir === 'download' && perc === 100) {
cbProgress({ prog: 100, p: fileTotalSize, m: 'upload' })
}
}
//fileTotalHash
// console.log(`calc hash for fileTotalSize[${fileTotalSize}]...`)
let fileTotalHash = await calcHash(bb)
// console.log(`calc hash for fileTotalSize[${fileTotalSize}] done`, fileTotalHash)
//send check-total-hash
// console.log(`send check-total-hash...`)
let resUpCkt = await send('upload-controller', { mode: 'check-total-hash', fileHash: fileTotalHash, filename: fileTotalName, fileSize: fileTotalSize }, { dataType: 'json' })
// console.log('resUpCkt', resUpCkt)
// resUpCkt {
// path: 'uploadTemp\\2429b7ef08ce6ba9',
// bAllExist: false,
// bAllSize: false,
// bAllHash: false,
// bSls: true,
// slks: [
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
// ]
// }
//check
if (resUpCkt.bAllHash) {
// console.log('已有上傳大檔')
//cbProgressMerge
cbProgressMerge({ prog: 100, m: 'download' }) //觸發上傳完畢後之下載回應, 故m須為download
//resMg, 直接回傳
let resMg = {
filename: fileTotalName,
path: resUpCkt.path,
}
return resMg
}
//針對伺服器上已有切片檔案計算hash與比對
if (resUpCkt.slks.length > 0) {
// console.log('receive slks...', resUpCkt.slks[0], size(resUpCkt.slks))
//fileSliceHashs
let fileSliceHashs = []
// let n = Math.max(resUpCkt.slks.length, 1)
// let nr = Math.floor(n / 100)
for (let k = 0; k < resUpCkt.slks.length; k++) {
// if (k % nr === 0) {
// console.log(`calc hash for slices`, round(k / resUpCkt.slks.length * 100, 1), '%')
// }
//i
let i = resUpCkt.slks[k]
//start
let start = i * sizeSlice
//end
let end = Math.min(start + sizeSlice, fileTotalSize)
//chunk
let chunk = bb.slice(start, end)
//fileSliceHash
let fileSliceHash = await calcHash(chunk)
// console.log('fileSliceHash', fileSliceHash)
//push
fileSliceHashs.push({
i,
h: fileSliceHash,
})
}
// console.log('fileSliceHashs', fileSliceHashs)
//send check-slices-hash
// console.log(`send check-slices-hash...`)
let resUpCks = await send('upload-controller', { mode: 'check-slices-hash', fileHash: fileTotalHash, fileSliceHashs }, { dataType: 'json' })
// console.log('resUpCks', resUpCks)
// resUpCks {
// slks: [
// 0, 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
// ]
// }
//update, 伺服器針對各切片計算hash與比對, 回傳resUpCks.slks代表hash一致的切片編號, 非一致hash的切片則須重傳, 尚未傳切片亦須繼續傳
resUpCkt.slks = resUpCks.slks
}
//packageId
let packageId = fileTotalHash
// console.log('packageId', packageId)
//upload slice
// console.log(`upload slice...`)
for (let i = 0; i < chunkTotal; i++) {
//check
if (resUpCkt.slks.indexOf(i) >= 0) {
// console.log('已有上傳切片檔')
cbProgressSlice({ prog: 100, m: 'upload' }) //直接觸發更新進度
continue
}
//start
let start = i * sizeSlice
//end
let end = Math.min(start + sizeSlice, fileTotalSize)
//chunk
let chunk = bb.slice(start, end)
//send slice
let hd = { //用header傳key與value時, key不分大小寫, 故使用kebabCase
'chunk-index': i,
'chunk-total': chunkTotal,
'package-id': packageId,
}
await send('slice', chunk, { headers: hd, dataType: 'blob', cbProgress: cbProgressSlice })
// console.log('resSl', resSl)
}
//send merge-slices-push
// console.log(`send merge-slices-push...`)
let resUpMgp = await send('upload-controller', { mode: 'merge-slices-push', fileHash: fileTotalHash, chunkTotal }, { dataType: 'json' })
// console.log('resUpMgp', resUpMgp)
//checkMerging
let checkMerging = () => {
let pm = genPm()
//queueId
let queueId = resUpMgp.queueId
// console.log('queueId', queueId)
let t = setInterval(() => {
//send merge-slices-get
// console.log(`send merge-slices-get...`)
send('upload-controller', { mode: 'merge-slices-get', fileHash: fileTotalHash, filename: fileTotalName, queueId }, { dataType: 'json' })
.then((res) => {
// console.log('res', res)
// res => {
// queueId,
// state,
// filename,
// path,
// msg: ...
// }
//check
if (res.state === 'success') {
//clearInterval
clearInterval(t)
//cbProgressMerge
cbProgressMerge({ prog: 100, m: 'download' }) //觸發上傳完畢後之下載回應, 故m須為download
//resolve, state為'success'時提取msg回傳
pm.resolve(res.msg)
}
else if (res.state === 'error') {
console.log('merge-slices-get error', res)
//clearInterval
clearInterval(t)
//reject, state為'error'時會於msg提供錯誤訊息
pm.resolve(res.msg)
}
})
.catch((err) => {
console.log('merge-slices-get catch', err)
//可能發生網路斷訊錯誤, 不clearInterval, 持續輪循測試合併大檔之狀態
})
}, 2000)
return pm
}
//checkMerging
let resMg = await checkMerging()
// console.log('resMg', resMg)
// console.log(`upload slice done`)
return resMg
}
//execute
async function execute(func, input, cbProgress) {
//msg
let msg = {
// _mode: mode,
// clientId,
func,
input,
}
//sendData
let state = ''
let res = null
await sendData('basic', msg, cbProgress)
.then((msg) => {
// console.log('msg', msg)
//check, 若為字串為錯誤訊息
if (isestr(msg)) {
state = 'error'
res = msg
return
}
//check, 若為非物件為非預期錯誤
if (!iseobj(msg)) {
console.log('msg is not an effective object', msg)
state = 'error'
res = 'msg is not an effective object'
return
}
//check, 若不存在output為非預期錯誤, msg格式為{func,input,output}但input會刪除
if (!haskey(msg, 'output')) {
console.log('invalid msg.output', msg)
state = 'error'
res = 'invalid msg.output'
return
}
state = 'success'
res = msg.output
})
.catch((msg) => {
state = 'error'
res = msg
})
//check
if (state === '') {
// console.log('invalid state', r)
eeEmit('error', `invalid state`)
return Promise.reject('invalid state')
}
//check
if (state === 'error') {
// console.log('send data error', r)
// eeEmit('error', res) //一般錯誤會嘗試n次, 每次也都會emit, 故此處不再基於已知state='error'時再emit
return Promise.reject(res)
}
return res
}
//upload
async function upload(filename, input, cbProgress) {
//bb
let bb = input
return sendDataSlice(filename, bb, cbProgress)
}
//downloadNodejs
async function downloadNodejs(fileId, cbProgress, opt = {}) {
//send download
let msg = { fileId }
let resMg = await send('download', msg, { ...opt, dataType: 'json', cbProgress })
// console.log('resMg', resMg)
return resMg
}
//downloadBrowser
async function downloadBrowser(fileId, cbProgress, opt = {}) {
//交由瀏覽器下載與管理故無法監聽進度, 不使用cbProgress
//token
let token = getToken()
if (ispm(token)) {
token = await token
}
// console.log('token', token)
//send download-get-filename
let msg = { fileId }
let resMg = await send('download-get-filename', msg, { dataType: 'json' })
// console.log('resMg', resMg)
//filename
let filename = get(resMg, 'filename', '')
// console.log('filename', filename)
//urlUse
let urlUse = getUrlUse('download-get')
// console.log('urlUse', urlUse)
//url
let url = `${urlUse}?fileId=${fileId}&token=${token}`
// console.log('url', url)
//透過a元素打url下載, 讓瀏覽器認定為直接下載模式, 由瀏覽器展示下載進度與排入正在下載清單
let a = document.createElement('a')
a.href = url
a.download = filename
a.click()
return filename
}
//download
async function download(fileId, cbProgress, opt = {}) {
if (env === 'browser') {
return downloadBrowser(fileId, cbProgress, opt)
}
else {
return downloadNodejs(fileId, cbProgress, opt)
}
}
//save
ee.execute = execute
ee.upload = upload
ee.download = download
return ee
}
export default WConverhpClient