@alanchenchen/browserbuffer
Version:
A simple implementation for browser to switch string between buffer
293 lines (277 loc) • 11 kB
JavaScript
/**
* @description 插件的入口模块。webpack的entry
*/
/**!
* @name BrowserBuffer
* @author Alan chen
* @since 2019/09/27
* @license 996.ICU
*/
module.exports = class BrowserBuffer {
constructor() {
this._init()
this.version = require('../package.json').version
this.createdBy = 'alanchenchen@github.com'
}
/**
* 初始化a标签创建
*
* @private
*/
_init() {
this._downloadElement = document.createElement('a')
this._uploadElement = document.createElement('input')
this._uploadElement.type = 'file'
}
/**
* 区分data的type,获取data的构造器名称
*
* @private
* @param {*} data
* @returns {String} blob、arrayBuffer、string、object或undefined其中一种
*/
_specifyData(data) {
if(Object.prototype.toString.call(data) == '[object Blob]') {
return 'blob'
}
else if(Object.prototype.toString.call(data) == '[object ArrayBuffer]') {
return 'arrayBuffer'
}
else if(typeof data == 'string') {
return 'string'
}
else if(Object.prototype.toString.call(data) == '[object Object]') {
return 'object'
}
}
/**
* 写入buffer
*
* @private
* @param {Blob} buf 必须是Blob对象
* @param {String} filename 文件名,可以带后缀名,如果带后缀名,则以后缀名为主,不带则取Blob对象type指定的MIME信息
*/
async _saveBuffer(buf, filename, charset) {
const result = await this.toString(buf, {
encode: 'dataURL',
MIME: buf.type,
charset: charset
})
this._downloadElement.href = result
this._downloadElement.download = filename
this._downloadElement.click()
}
/**
* 写入远程服务器文件
*
* @private
* @param {URL} path 同源的文件url路径,必须同源,否则会跳转到对应超链接而不是下载
* @param {String} filename 文件名,可以带后缀名,可以不带
*/
_saveURL(path, filename) {
this._downloadElement.href = path
this._downloadElement.download = filename
this._downloadElement.click()
}
/**
* 通过打开系统dialog读取本地文件数据
*
* @param {Object} opt
* @param {Array} opt.accept 允许用户上传任何类型的文件,但不能完全限制,数组项可以是MIME信息,也可以是文件后缀名,还可以是image/*这种,默认允许所有类型
* @param {Boolean} opt.multiple 是否支持多选,默认不支持,如果支持,可以通过键盘Shift或Control来多选
* @param {String} opt.encode 文件的编码格式,utf8、base64和binary字符串其中之一,默认为base64
* @returns {Promise} reslove一个数组,数组包含每个文件的数据对象,catch一个错误对象
*/
readFile({
accept=['*'],
multiple=false,
encode='base64'
} = {}) {
this._uploadElement.accept = accept.join(',')
this._uploadElement.multiple = multiple
const readOneFile = (file, encode='base64') => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
let fn = null
switch (encode) {
case 'utf8':
fn = 'readAsText'
break
case 'base64':
fn = 'readAsDataURL'
break
case 'binary':
fn = 'readAsArrayBuffer'
break
default:
fn = 'readAsDataURL'
}
reader[fn](file)
reader.onload = () => {
let result = reader.result
if(encode == 'base64') {
result = reader.result.split('base64,')[1]
}
resolve(result)
}
reader.onerror = () => {
reader.abort()
reject('some erros occured')
}
})
}
return new Promise((resolve, reject) => {
this._uploadElement.onchange = async (e) => {
let fileData = []
let fileslist = this._uploadElement.files || e.path[0].files
try {
for(let key of Object.keys(fileslist)) {
fileData[key] = {}
fileData[key].name = fileslist[key].name
fileData[key].type = fileslist[key].type
fileData[key].size = fileslist[key].size
fileData[key].lastModified = fileslist[key].lastModified
fileData[key].lastModifiedDate = fileslist[key].lastModifiedDate
fileData[key].data = await readOneFile(fileslist[key], encode)
}
resolve(fileData)
} catch (error) {
reject(error)
}
}
this._uploadElement.click()
})
}
/**
* 写入数据到本地文件
*
* @param {Object} opt
* @param {String} opt.filename 文件名,可以带后缀名,如果带后缀名,则以后缀名为主,不带则取MIME信息
* @param {String} opt.dataPath 同源的文件url路径,如果不为空,则只会下载此同源服务器路径的文件,而忽略data
* @param {String | Object | Blob | ArrayBuffer} opt.data 写入的数据
* @param {String} opt.MIME 仅当filename不带后缀名时生效,决定转换后数据的文件类型,默认为text/plain,txt文本。filename如果带上后缀名,则会覆盖MIME。
* @param {String} opt.charset 文件的编码格式,默认为utf8
* @returns {Promise} 因为是async函数,所以返回一个promise
*/
async writeFile({
filename='',
dataPath='',
data,
MIME='text/plain',
charset='utf8'
} = {}) {
if(Boolean(dataPath.trim())) {
this._saveURL(dataPath, filename)
return
}
else {
let buf
const dataType = this._specifyData(data)
if(dataType == 'string' || dataType == 'arrayBuffer' || dataType == 'blob') {
buf = new Blob([data], {
type: MIME
})
}
else if(dataType == 'object') {
buf = new Blob([JSON.stringify(data)], {
type: MIME
})
}
else {
throw new Error('data must be string or object or Blob or ArrayBuffer')
}
await this._saveBuffer(buf, filename, charset)
}
}
/**
* 将一个字符串(或Blob对象、或ArrayBuffer)转成一个ArrayBuffer
*
* @param {String | Blob | ArrayBuffer} data 需要转换的数据
* @param {String} encode 编码格式,默认是utf8,可选base64,需要区分utf8和base64转换的方式完全不同!
* @returns {Promise} reslove一个ArrayBuffer,catch一个错误对象
*/
from(data, encode="utf8") {
try {
const blobData = new Blob([data])
return new Promise((resolve, reject) => {
if (encode === "utf8") {
const file = new FileReader()
file.readAsArrayBuffer(blobData)
file.onload = () => {
resolve(file.result)
}
file.onerror = () => {
file.abort()
reject('some erros occured')
}
} else if (
encode === "base64"
&& typeof data === "string"
) {
const ut8String = atob(data)
let len = ut8String.length
const byteArr = new Uint8Array(len)
while(len--) {
byteArr[len] = ut8String.charCodeAt(len)
}
resolve(byteArr)
} else {
throw new Error("encode must be utf8 or base64")
}
})
} catch (error) {
throw new Error(`data must be string or object or Blob or ArrayBuffer, ${error}`)
}
}
/**
* 将一个Blob对象、或ArrayBuffer转成一个utf8、base64或dataURL三种格式之一的字符串
*
* @param {Blob | ArrayBuffer} data 需要转换的数据
* @param {Object} opt
* @param {String} opt.encode 转换后的字符串编码格式,有utf8、base64和dataURL三种可选,默认为utf8
* @param {String} opt.MIME 仅当编码格式为dataURL生效,决定转换后数据的文件类型,默认为text/plain,txt文本
* @param {String} opt.charset 仅当编码格式为dataURL生效,决定转换后数据的编码类型,默认为utf8编码
* @returns {Promise} reslove一个根据编码格式返回纯字符串或base64字符串或带dataURL的base64字符串,catch一个错误对象
*/
toString(data, {encode='utf8', MIME='text/plain', charset='utf8'} = {}) {
try {
const blobData = new Blob([data], {type: MIME})
let fn = null
switch (encode) {
case 'utf8':
fn = 'readAsText'
break
case 'base64':
fn = 'readAsDataURL'
break
case 'dataURL':
fn = 'readAsDataURL'
break
default:
fn = 'readAsText'
}
return new Promise((resolve, reject) => {
const file = new FileReader()
file[fn](blobData)
file.onload = () => {
if(encode == 'utf8') {
resolve(file.result)
}
else {
let result = file.result.split(';').join(`;charset=${charset};`)
if(encode == 'base64') {
result = result.split('base64,')[1]
}
resolve(result)
}
}
file.onerror = () => {
file.abort()
reject('some erros occured')
}
})
} catch(error) {
throw new Error(`data must be string or object or Blob or ArrayBuffer, ${error}`)
}
}
}