1 files added
5 files modified
| | |
| | | if (process.env.TARO_ENV === 'weapp') { |
| | | this._saveCookies(response.cookies); |
| | | } |
| | | // 捕获响应 |
| | | options && options.onCapture && options.onCapture({ |
| | | url: this._createUrlPrefix(options) + url, |
| | | request: data, |
| | | response: { ...response.data }, |
| | | httpCode: response.statusCode, |
| | | }); |
| | | /** |
| | | * @type {{state: {code, http, msg}, data: Object}} |
| | | * @example response.state.code |
| | |
| | | resolve(this._transformResponseData(responseData, options)); |
| | | }, |
| | | fail: error => { |
| | | if (typeof options.silence === 'undefined' || !options.silence) { |
| | | this._resolveCaughtNetErr(error); |
| | | } |
| | | // 处理响应 |
| | | this._resolveCaughtNetErr(error, options, msg => { |
| | | // 捕获响应 |
| | | options && options.onCapture && options.onCapture({ |
| | | url: this._createUrlPrefix(options) + url, |
| | | request: data, |
| | | httpCode: error && error.status, |
| | | httpMsg: msg + (error.message ? (' / ' + error.message) : ''), |
| | | }); |
| | | }); |
| | | reject(null); |
| | | }, |
| | | }); |
| | |
| | | /** |
| | | * 解析捕获的网络错误 |
| | | * @param err |
| | | * @param options |
| | | * @param callback |
| | | * @private |
| | | */ |
| | | _resolveCaughtNetErr(err) { |
| | | _resolveCaughtNetErr(err, options, callback) { |
| | | let msg = ''; |
| | | if (err && err.status) { |
| | | switch (err.status) { |
| | |
| | | } else { |
| | | msg += '解析通讯数据异常!'; |
| | | } |
| | | callback(msg); |
| | | setTimeout(() => { |
| | | this._message('fail', msg); |
| | | if (typeof options.silence === 'undefined' || !options.silence) { |
| | | this._message('fail', msg); |
| | | } |
| | | }, 20); |
| | | } |
| | | |
| | | /** |
| | | * 转换响应体 |
| | | * @param response |
| | | * @param options |
| | | * @returns {Object|{}|null} |
| | | * @private |
| | | */ |
| | |
| | | }; |
| | | } |
| | | // 如果有匹配服务器,使用指定的服务器地址 |
| | | if (typeof this._data.hostList[server] !== 'undefined') { |
| | | else if (typeof this._data.hostList[server] !== 'undefined') { |
| | | this._data.activeHost[typeName] = { |
| | | name: server, |
| | | host: this._data.hostList[server], |
| | |
| | | |
| | | import { Fetcher } from '@components/bases/Fetcher'; |
| | | import { Tools } from '@components/common/Tools'; |
| | | import { $fileTrans } from '@components/common/FileTransform'; |
| | | |
| | | export class Bridge { |
| | | |
| | | constructor() { |
| | | this._data = { |
| | | count: 100, |
| | | fileSaved: {}, // 已保存图片名称列表 { 'blob:***' : 'bridge:***' } |
| | | fileLoaded: {}, // 已读取图片名称列表 { 'bridge:***' : 'blob:***' } |
| | | }; |
| | | this._receives = {}; |
| | | this._earlyInvok = []; |
| | |
| | | return Fetcher.prototype.transKeyName(type, json); |
| | | } |
| | | |
| | | /* ----- 文件系统 ----- */ |
| | | |
| | | /** |
| | | * 保存文件到 java 端,并返回文件名 |
| | | * @param objUrl |
| | | * @param callback |
| | | * @param onError |
| | | */ |
| | | fileSave(objUrl = '', callback, onError) { |
| | | // 非 ObjectURL 跳过 |
| | | if (objUrl.indexOf('blob:') < 0) { |
| | | callback(objUrl); |
| | | return; |
| | | } |
| | | if (this._data.fileSaved[objUrl]) { |
| | | callback(this._data.fileSaved[objUrl]); |
| | | return; |
| | | } |
| | | // 分段存储 |
| | | const saveFileChunk = (baseData, index) => { |
| | | const writeData = { |
| | | fileName: baseData.fileName, |
| | | currentIdx: index, |
| | | totalIdx: baseData.total, |
| | | data: baseData.baseArr[index], |
| | | }; |
| | | this.invoking('img_write', writeData, res => { |
| | | if (res.result === false) { |
| | | Tools.toast('离线图片存储:' + res.msg); |
| | | onError({ |
| | | method: 'img_write', |
| | | request: { |
| | | fileName: writeData.fileName, |
| | | currentIdx: writeData.currentIdx, |
| | | totalIdx: writeData.total, |
| | | data: (writeData.data || '').substr(0, 10) |
| | | + '...(共' + (writeData.data || '').length + '个base64字符)', |
| | | }, |
| | | response: res, |
| | | }); |
| | | return; |
| | | } |
| | | // 按分段递归保存 |
| | | if (index < baseData.total - 1) { |
| | | setTimeout(() => { |
| | | saveFileChunk(baseData, index + 1); |
| | | }, 10); |
| | | } |
| | | // 已完成 |
| | | else { |
| | | callback && callback('bridge:' + baseData.fileName); |
| | | } |
| | | }); |
| | | }; |
| | | $fileTrans.transObjUrlToBaseData(objUrl, baseData => { |
| | | this._data.fileSaved[objUrl] = 'bridge:' + baseData.fileName; |
| | | saveFileChunk(baseData, 0); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 从 java 读取文件 base64 |
| | | * @param bridgeName |
| | | * @param callback |
| | | * @param onError |
| | | */ |
| | | fileLoad(bridgeName = '', callback, onError) { |
| | | // 非存储地址,跳过 |
| | | if (bridgeName.indexOf('bridge:') < 0) { |
| | | callback(bridgeName); |
| | | return; |
| | | } |
| | | if (this._data.fileLoaded[bridgeName]) { |
| | | callback(this._data.fileLoaded[bridgeName]); |
| | | return; |
| | | } |
| | | const fileName = bridgeName.split(':')[1]; |
| | | const chunkSize = $fileTrans.getChunkSize(); |
| | | const baseArr = []; |
| | | let totalSize = 0; |
| | | let totalCount = 0; |
| | | const loadFileChunk = (index) => { |
| | | const loadData = { |
| | | fileName, |
| | | offset: chunkSize * index, |
| | | length: chunkSize, |
| | | }; |
| | | this.invoking('img_read', loadData, res => { |
| | | if (res.result === false) { |
| | | Tools.toast('离线图片读取:' + res.msg); |
| | | onError({ |
| | | method: 'img_read', |
| | | request: { |
| | | ...loadData, |
| | | totalCount, |
| | | }, |
| | | response: { |
| | | result: res.result, |
| | | msg: res.msg, |
| | | 'total_size': res.totalSize, |
| | | data: (res.data || '').substr(0, 10) |
| | | + '...(共' + (res.data || '').length + '个base64字符)', |
| | | }, |
| | | }); |
| | | return; |
| | | } |
| | | if (totalSize === 0) { |
| | | totalSize = res.totalSize; |
| | | totalCount = Math.ceil(res.totalSize / chunkSize); |
| | | } |
| | | baseArr.push(res.data); |
| | | // 按分段递归读取 |
| | | if (totalCount > 1 && totalCount - 1 > index) { |
| | | loadFileChunk(index + 1); |
| | | } |
| | | // 读取完成 |
| | | else { |
| | | const baseData = { |
| | | baseArr, |
| | | fileName, |
| | | }; |
| | | try { |
| | | $fileTrans.transBaseDataToObjUrl(baseData, objUrl => { |
| | | this._data.fileLoaded[bridgeName] = objUrl; |
| | | callback && callback(objUrl); |
| | | }); |
| | | } catch (e) { |
| | | onError({ |
| | | method: 'img_read@merge_after_base64_loaded', |
| | | request: { |
| | | ...loadData, |
| | | totalCount, |
| | | }, |
| | | response: { |
| | | result: res.result, |
| | | msg: res.msg, |
| | | 'total_size': res.totalSize, |
| | | data: (res.data || '').substr(0, 10) |
| | | + '...(共' + (res.data || '').length + '个base64字符)', |
| | | }, |
| | | base64Arr: baseData.baseArr.map(baseItem => (baseItem || []).substr(0, 10) |
| | | + '...(共' + (res.data || '').length + '个base64字符)'), |
| | | message: 'Base64合并解析异常!', |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | loadFileChunk(0); |
| | | } |
| | | |
| | | /** |
| | | * 从 java 端移除文件 |
| | | * @param bridgeName |
| | | */ |
| | | fileRemove(bridgeName = '') { |
| | | if (bridgeName.indexOf('bridge:') < 0) { |
| | | return; |
| | | } |
| | | const fileName = bridgeName.split(':')[1]; |
| | | this.invoking('img_del', { fileName }); |
| | | // 移除 |
| | | Object.keys(this._data.fileSaved).forEach(objUrl => { |
| | | if (bridgeName === this._data.fileSaved[objUrl]) { |
| | | delete this._data.fileSaved[objUrl]; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 文件存储批量操作服务 |
| | | * @param type |
| | | * @param names |
| | | * @param callback |
| | | */ |
| | | fileStore(type, names = [], callback) { |
| | | if (!names || names.length === 0) { |
| | | callback && callback([]); |
| | | return; |
| | | } |
| | | if (typeof names === 'string') { |
| | | names = names.split(','); |
| | | } |
| | | // 保存 |
| | | if (type === 'save') { |
| | | const list = []; |
| | | const save = index => { |
| | | this.fileSave(names[index], bridgeName => { |
| | | list.push(bridgeName); |
| | | // 递归下一个 |
| | | if (index < names.length - 1) { |
| | | setTimeout(() => { |
| | | save(index + 1); |
| | | }, 10); |
| | | } |
| | | // 完成 |
| | | else { |
| | | callback && callback(list); |
| | | } |
| | | }, err => { |
| | | callback && callback(null, err); |
| | | }); |
| | | }; |
| | | save(0); |
| | | } |
| | | // 读取 |
| | | else if (type === 'load') { |
| | | const list = []; |
| | | const load = index => { |
| | | this.fileLoad(names[index], objUrl => { |
| | | list.push(objUrl); |
| | | // 递归下一个 |
| | | if (index < names.length - 1) { |
| | | setTimeout(() => { |
| | | load(index + 1); |
| | | }, 10); |
| | | } |
| | | // 完成 |
| | | else { |
| | | callback && callback(list); |
| | | } |
| | | }, err => { |
| | | callback && callback(null, err); |
| | | }); |
| | | }; |
| | | load(0); |
| | | } |
| | | // 移除 |
| | | else if (type === 'remove') { |
| | | names.forEach((name, index) => { |
| | | setTimeout(() => { |
| | | this.fileRemove(name); |
| | | }, 10 * index); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | // 全局服务实例 |
New file |
| | |
| | | /** |
| | | * 文件转换 |
| | | * @author Tevin |
| | | */ |
| | | |
| | | import moment from 'moment'; |
| | | |
| | | export class FileTransform { |
| | | |
| | | constructor() { |
| | | this._data = { |
| | | chunkSize: 20 * 1024, |
| | | }; |
| | | } |
| | | |
| | | getChunkSize() { |
| | | return this._data.chunkSize; |
| | | } |
| | | |
| | | setChunkSize() { |
| | | return this._data.chunkSize; |
| | | } |
| | | |
| | | /** |
| | | * 转换 ObjectURL 为 base64 数据体 |
| | | * @param objUrl |
| | | * @param callback |
| | | */ |
| | | transObjUrlToBaseData(objUrl, callback) { |
| | | this.convertObjectUrlToBlob(objUrl, blob => { |
| | | this.convertBlobToBase64(blob, base64 => { |
| | | const mime = base64.split(',')[0].match(/:(.*?);/)[1]; |
| | | const fileName = this._makeFileName(mime); |
| | | const baseArr = this.splitBase64ToArray(base64); |
| | | callback && callback({ |
| | | fileName, |
| | | baseArr, |
| | | total: baseArr.length, |
| | | }); |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 转换 base64 数据体为 ObjectURL |
| | | * @param baseData |
| | | * @param callback |
| | | */ |
| | | transBaseDataToObjUrl(baseData, callback) { |
| | | const base64 = baseData.baseArr.join(''); |
| | | this.convertBase64ToFile(base64, baseData.fileName, file => { |
| | | this.convertFileToObjectUrl(file, objUrl => { |
| | | callback && callback(objUrl); |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | convertBlobToBase64(blob, callback) { |
| | | const reader = new FileReader(); |
| | | reader.onload = evt => { |
| | | callback && callback(evt.target.result); |
| | | }; |
| | | reader.readAsDataURL(blob); |
| | | } |
| | | |
| | | convertObjectUrlToBlob(objUrl, callback) { |
| | | const xhr = new XMLHttpRequest(); |
| | | xhr.open('GET', objUrl, true); |
| | | xhr.responseType = 'blob'; |
| | | xhr.onload = function () { |
| | | if (this.status === 200) { |
| | | callback && callback(this.response); |
| | | } else { |
| | | /* eslint-disable prefer-promise-reject-errors */ |
| | | callback && callback(null, { status: this.status }); |
| | | } |
| | | }; |
| | | xhr.send(); |
| | | } |
| | | |
| | | convertBase64ToFile(base64, fileName, callback) { |
| | | const arr = base64.split(','); |
| | | const mime = arr[0].match(/:(.*?);/)[1]; |
| | | const bstr = atob(arr[1]); |
| | | let n = bstr.length; |
| | | const u8arr = new Uint8Array(n); |
| | | while (n--) { |
| | | u8arr[n] = bstr.charCodeAt(n); |
| | | } |
| | | fileName = fileName || this._makeFileName(mime); |
| | | const file = new File([u8arr], fileName, { type: mime }); |
| | | callback && callback(file); |
| | | } |
| | | |
| | | convertFileToObjectUrl(file, callback) { |
| | | let URLMaker = URL; |
| | | if (window) { |
| | | URLMaker = window.URL || window.webkitURL; |
| | | } |
| | | const url = URLMaker.createObjectURL(file); |
| | | callback && callback(url); |
| | | } |
| | | |
| | | // 生成文件名 |
| | | _makeFileName(mime) { |
| | | const curMoment = moment(); |
| | | const name = 'IMG' + curMoment.format('-YYYYMMDD-HHmmss-') + |
| | | curMoment.format('x').substr(-3) + '-' + parseInt(Math.random() * 10000); |
| | | const mimeTypes = { |
| | | 'image/jpeg': 'jpg', |
| | | 'image/gif': 'gif', |
| | | 'image/png': 'png', |
| | | 'image/svg+xml': 'svg', |
| | | 'image/webp': 'webp', |
| | | 'image/tiff': 'tif', |
| | | 'text/plain': 'txt', |
| | | 'text/css': 'css', |
| | | 'text/html': 'html', |
| | | 'text/xml': 'xml', |
| | | 'text/javascript': 'js', |
| | | 'application/x-javascript': 'js', |
| | | 'audio/mpeg': 'mp3', |
| | | 'video/mp4': 'mp4', |
| | | 'video/webm': 'webm', |
| | | 'application/zip': 'zip', |
| | | 'application/rar': 'rar', |
| | | 'application/pdf': 'pdf', |
| | | 'application/rtf': 'rtf', |
| | | }; |
| | | return name + '.' + mimeTypes[mime]; |
| | | } |
| | | |
| | | splitBase64ToArray(base64) { |
| | | const count = Math.ceil(base64.length / this._data.chunkSize); |
| | | if (count === 1) { |
| | | return [base64]; |
| | | } |
| | | const array = []; |
| | | for (let i = 0; i < count; i++) { |
| | | const chunk = base64.substr(this._data.chunkSize * i, this._data.chunkSize); |
| | | array.push(chunk); |
| | | } |
| | | return array; |
| | | } |
| | | |
| | | } |
| | | |
| | | export const $fileTrans = new FileTransform(); |
| | |
| | | img.onerror = null; |
| | | callback && callback(false); |
| | | }; |
| | | img.src = 'http://tt.zhiheiot.com/static/online.png?t=' + Date.now(); |
| | | img.src = 'http://gz.zhiheiot.com/disp/download/buildArchive/online.png?t=' + Date.now(); |
| | | } |
| | | // 网络未开启 |
| | | else { |
| | |
| | | this.showImg = false; |
| | | }, |
| | | handleFail(msg) { |
| | | if (typeof msg === 'object') { |
| | | msg = msg.message; |
| | | } |
| | | Taro.showToast({ |
| | | title: msg, |
| | | icon: 'none', |
| | |
| | | }); |
| | | }, |
| | | $uploadImage(callback) { |
| | | let url = $fetchCommon.getUploadImgURL(); |
| | | if (url.indexOf('http') < 0) { |
| | | url = $hostBoot.getHost() + url; |
| | | } |
| | | const uploadTeam = []; |
| | | const imgs = []; |
| | | const files = []; |
| | | this.files.forEach(file => { |
| | | if (file.type === 'btn') { |
| | | return; |
| | | } |
| | | // 临时文件才上传 |
| | | if ( |
| | | file.url.indexOf('blob') >= 0 || |
| | | file.url.indexOf('wxfile') >= 0 || |
| | | file.url.indexOf('http://tmp/') >= 0 |
| | | ) { |
| | | let header = {}; |
| | | if (process.env.TARO_ENV === 'weapp') { |
| | | const localCookies = JSON.parse( |
| | | Taro.getStorageSync('cookies') || '{}' |
| | | ); |
| | | const cookiesArr = []; |
| | | Object.keys(localCookies).forEach(key => { |
| | | cookiesArr.push(key + '=' + localCookies[key]); |
| | | }); |
| | | header['Cookie'] = cookiesArr.join('; '); |
| | | } |
| | | uploadTeam.push( |
| | | new Promise((resolve, reject) => { |
| | | Taro.uploadFile({ |
| | | url, |
| | | header, |
| | | filePath: file.url, |
| | | fileName: this.fileNames[file.url], |
| | | name: 'file', |
| | | formData: {}, |
| | | success(res) { |
| | | const res2 = |
| | | typeof res.data === 'string' |
| | | ? JSON.parse(res.data) |
| | | : res.data; |
| | | if (res2.state.code === 2000) { |
| | | resolve( |
| | | $fetchCommon.transImgPath( |
| | | 'fix', |
| | | res2.data.src || |
| | | res2.data.file || |
| | | res2.data.url |
| | | ) |
| | | ); |
| | | } else { |
| | | reject({ message: res2.state.msg }); |
| | | } |
| | | }, |
| | | cancel() { |
| | | reject({ message: '上传图片已取消!' }); |
| | | }, |
| | | fail() { |
| | | reject({ message: '上传图片失败!' }); |
| | | }, |
| | | }); |
| | | }) |
| | | ); |
| | | } |
| | | // 其他类型视为 url,忽略 |
| | | else { |
| | | uploadTeam.push(Promise.resolve(file.url)); |
| | | } |
| | | file.fileName = this.fileNames[file.url]; |
| | | files.push(file); |
| | | }); |
| | | Promise.all(uploadTeam) |
| | | .then(res => { |
| | | uploadImage(files, (state, res) => { |
| | | if (state === 'success') { |
| | | this.itemRes.onChange(res); |
| | | setTimeout(() => { |
| | | callback('success'); |
| | | }, 0); |
| | | }) |
| | | .catch(err => { |
| | | callback('error', err); |
| | | }); |
| | | callback(state); |
| | | }, 10); |
| | | } else if (state === 'error') { |
| | | callback(state, res); |
| | | } |
| | | }); |
| | | }, |
| | | }, |
| | | mounted() { |
| | |
| | | } |
| | | }, |
| | | }; |
| | | |
| | | // 图片上传节流 |
| | | const _readyUpload = {}; |
| | | |
| | | export const uploadImage = (files, callback) => { |
| | | if (!files || files.length === 0) { |
| | | callback('success', []); |
| | | return; |
| | | } |
| | | let url = $fetchCommon.getUploadImgURL(); |
| | | if (url.indexOf('http') < 0) { |
| | | url = $hostBoot.getHost() + url; |
| | | } |
| | | const uploadTeam = []; |
| | | const imgs = []; |
| | | files.forEach(file => { |
| | | // 临时文件才上传 |
| | | if ( |
| | | file.url.indexOf('blob') >= 0 || |
| | | file.url.indexOf('wxfile') >= 0 || |
| | | file.url.indexOf('http://tmp/') >= 0 |
| | | ) { |
| | | const header = {}; |
| | | if (process.env.TARO_ENV === 'weapp') { |
| | | const localCookies = JSON.parse(Taro.getStorageSync('cookies') || '{}'); |
| | | const cookiesArr = []; |
| | | Object.keys(localCookies).forEach(key => { |
| | | cookiesArr.push(key + '=' + localCookies[key]); |
| | | }); |
| | | header['Cookie'] = cookiesArr.join('; '); |
| | | } |
| | | const requestFile = { |
| | | url, |
| | | filePath: file.url, |
| | | fileName: file.fileName || '', |
| | | }; |
| | | uploadTeam.push( |
| | | new Promise((resolve, reject) => { |
| | | // 如果本次已上传过,直接返回地址 |
| | | if (_readyUpload[file.url] && _readyUpload[file.url].length > 5) { |
| | | resolve(_readyUpload[file.url]); |
| | | return; |
| | | } |
| | | // 上传 |
| | | Taro.uploadFile({ |
| | | ...requestFile, |
| | | header, |
| | | name: 'file', |
| | | formData: {}, |
| | | success(res) { |
| | | const res2 = |
| | | typeof res.data === 'string' |
| | | ? JSON.parse(res.data) |
| | | : res.data; |
| | | // 上传成功 |
| | | if (res2.state.code === 2000) { |
| | | const imgUrl = $fetchCommon.transImgPath( |
| | | 'fix', |
| | | res2.data.src || res2.data.file || res2.data.url |
| | | ); |
| | | _readyUpload[file.url] = imgUrl; |
| | | resolve(imgUrl); |
| | | } |
| | | // 上传失败 |
| | | else { |
| | | reject({ |
| | | ...requestFile, |
| | | response: res2, |
| | | message: res2.state.msg, |
| | | }); |
| | | } |
| | | }, |
| | | cancel(err) { |
| | | reject({ |
| | | ...requestFile, |
| | | message: '上传图片已取消!', |
| | | }); |
| | | }, |
| | | fail() { |
| | | reject({ |
| | | ...requestFile, |
| | | message: '上传图片失败!', |
| | | }); |
| | | }, |
| | | }); |
| | | }) |
| | | ); |
| | | } |
| | | // 其他类型视为 url,忽略 |
| | | else { |
| | | uploadTeam.push(Promise.resolve(file.url)); |
| | | } |
| | | }); |
| | | Promise.all(uploadTeam) |
| | | .then(res => { |
| | | callback('success', res); |
| | | }) |
| | | .catch(err => { |
| | | callback('error', err); |
| | | }); |
| | | }; |
| | | </script> |