ci-plus
Version:
ci组件库
513 lines (485 loc) • 20.1 kB
text/typescript
/**
* @module downFile
* @author : 卖女孩的小火柴
* !description : 下载文件方法
* @version : 1.0.2
* @since : 创建时间 2024-07-09 10:46:11
*/
import { ElMessage } from 'element-plus'
/**
* 下载文件函数
* @param blob 文件数据,可以是Blob对象或BlobPart数组的一部分
* @param fileName 下载后的文件名
* 将给定的blob对象转换为文件,并提供一个下载链接以供用户下载。
* 如果blob对象是JSON格式,则尝试解析为JavaScript对象,并显示相应的错误消息。
* 如果解析失败,则直接以原始格式下载blob对象。
*/
const downFileFn = function (
blob: Blob | BlobPart,
fileName: string,
resolve?: Function,
reject?: Function
) {
// 创建一个新的Blob对象,指定类型为application/json,假设blob是我们需要转换的Blob对象
let b = new Blob([blob], {
type: 'application/json'
})
// 创建一个FileReader对象用于读取Blob对象,创建一个新的FileReader实例
let reader = new FileReader()
// 使用FileReader的readAsText方法读取Blob对象 以文本形式读取Blob对象
reader.readAsText(b)
// 当数据读取完成时,处理结果
reader.onload = function (e) {
let text = e?.target?.result // 获取到的text
// console.log('text: ', text)
// 尝试将读取的文本解析为JSON对象
try {
let response = JSON.parse(text as string)
console.log(response) // 这里是解析后object对象
ElMessage.error(response.msg)
return reject && reject(response)
} catch (error) {
// 如果解析失败,将Blob对象转换为URL并创建下载链接
// console.error('Error parsing JSON', error)
const url = URL.createObjectURL(new Blob([b]))
let link: HTMLAnchorElement | null = document.createElement('a')
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
ElMessage.success('操作成功,请打开浏览器自带下载器查看文件!')
link = null
resolve && resolve({ msg: '操作成功,请打开浏览器自带下载器查看文件!' })
}
}
// 如果在读取过程中出错
reader.onerror = function () {
console.error('读取过程中出错.')
}
}
type Obj = {
[key: string]: any
}
interface Config {
method?: string // 请求方法,默认为 GET | POST
headers?: Object // 请求头
cbpercentage?: Function // 获取下载进度的回调函数
fileName?: string // 下载后的文件名
chunkSize?: number // 每次下载的块大小,默认为 10KB
body?: string // 请求体
}
const ajaxBox = {
downFile: function (blob: Blob | BlobPart, fileName: string) {
downFileFn(blob, fileName)
},
// 下载文件
/**
* 下载文件函数
* @param fillAddress string 表示服务端接口url地址
* @param fileName string 下载后的文件名
* @param method string 如果不想写get呢,你就写个null,但是你得写进去
* @param headers Object 请求头
* @param params Object 请求参数
* 将给定的blob对象转换为文件,并提供一个下载链接以供用户下载。
* 如果blob对象是JSON格式,则尝试解析为JavaScript对象,并显示相应的错误消息。
* 如果解析失败,则直接以原始格式下载blob对象。
*/
downFileFetch: function (
fillAddress: string,
fileName: string,
method?: string,
headers?: Obj,
params?: Obj,
cbpercentage?: Function
) {
let options = {
method: method || 'GET'
}
let _headers
if (options.method === 'GET' || method === 'get') {
_headers = {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
} else if (options.method === 'POST' || method === 'post') {
_headers = {
'Content-Type': 'application/json' //设置为json格式
}
}
// 判断请求是否为get请求,若是get请求,则将params参数拼接在url后面
if (method === 'GET') {
const queryString = new URLSearchParams(params).toString()
fillAddress = fillAddress + '?' + queryString
}
if (headers) {
options['headers'] = headers || _headers
}
if ((method === 'post' || method === 'POST') && params) {
options['body'] = JSON.stringify(params)
}
// 定义每次读取的数据块大小(字节)
let chunkSize = 10240 // 例如,10KB
// 如果在params中传递了chunkSize参数,则使用其值作为块大小
if (params && params.chunkSize) {
chunkSize = params.chunkSize
}
// 初始化已下载的字节数
let downloaded = 0
// 初始化 Blob 切片起始点
let start = 0
fetch(fillAddress, options)
.then((res) => {
// console.log('resssssss: ', res)
if (!res.ok) {
throw new Error('Network response was not ok')
}
return res.blob()
})
.then((blob) => {
// console.log('blob: ', blob)
const reader = new FileReader() // 创建 FileReader 对象
const readBlobInChunks = () => {
// 创建一个 Blob 切片,大小为 chunkSize
const chunkBlob = blob.slice(start, start + chunkSize)
start += chunkSize // 更新起始点
// 读取 Blob 切片
reader.readAsArrayBuffer(chunkBlob)
reader.onload = () => {
// 累加已下载的字节数
// downloaded += reader.result.byteLength
if (typeof reader.result === 'string' || reader.result === null) {
console.log('reader.result不为 ArrayBuffer')
// 处理字符串的情况,可能需要转换为 ArrayBuffer 或者其他处理逻辑
} else {
downloaded += reader.result.byteLength
}
// 计算下载进度百分比
const total = blob.size
const progress = ((downloaded / total) * 100).toFixed(2)
cbpercentage && cbpercentage(progress) // 如果传递了获取下载进度的回调函数,则调用回调函数传递进度百分比
console.log(`下载进度: ${progress}%`)
// 如果还有数据未读取,则继续读取
if (start < total) {
readBlobInChunks()
} else {
console.log('下载完成.')
// 处理下载完成的数据,例如将其保存或显示
// downFileFn(blob, fileName)
downFileFn(blob, fileName)
}
}
}
readBlobInChunks()
})
},
// 下载文件:2.0版本
/**
* 下载文件函数
* @param url string 必传 表示服务端接口url地址
* @param params Obj 可选 请求参数
* @param config Config 可选 请求配置
* Config Config {
method?: string, // 请求方法,默认为 GET | POST
headers?: Object, // 请求头,默认值根据method方法判断
cbpercentage?: Function // 获取下载进度的回调函数
fileName?: string, // 下载后的文件名默认:'附件'
chunkSize?: number, // 每次下载的块大小,默认为 10KB
}
* 将给定的blob对象转换为文件,并提供一个下载链接以供用户下载。
* 如果blob对象是JSON格式,则尝试解析为JavaScript对象,并显示相应的错误消息。
* 如果解析失败,则直接以原始格式下载blob对象。
*/
downFileFetchV2: function (url: string, params?: Obj, config?: Config) {
let options = {
method: config?.method || 'GET'
}
let _headers
let _fileName = config?.fileName || '附件'
if (options.method === 'GET' || options.method === 'get') {
_headers = {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
} else if (options.method === 'POST' || options.method === 'post') {
_headers = {
'Content-Type': 'application/json' // 设置为json格式
}
}
// 数组请求头: 优先使用传递的请求头,如果没有传递,则使用默认的请求头
options['headers'] = config?.headers || _headers
// 判断请求是否为get请求,若是get请求,则将params参数拼接在url后面
if (options.method === 'GET') {
const queryString = new URLSearchParams(params).toString()
url = url + '?' + queryString
}
// 处理post请求的参数
if ((options.method === 'post' || options.method === 'POST') && params) {
options['body'] = JSON.stringify(params)
}
// 定义每次读取的数据块大小(字节)
let chunkSize = config?.chunkSize || 10240 // 例如,10KB
// 初始化已下载的字节数
let downloaded = 0
// 初始化 Blob 切片起始点
let start = 0
return new Promise((resolve, reject) => {
fetch(url, options)
.then((res) => {
// console.log('resssssss: ', res)
if (!res.ok) {
throw new Error('Network response was not ok')
}
return res.blob()
})
.then((blob) => {
// console.log('blob: ', blob)
const reader = new FileReader() // 创建 FileReader 对象
const readBlobInChunks = () => {
// 创建一个 Blob 切片,大小为 chunkSize
const chunkBlob = blob.slice(start, start + chunkSize)
start += chunkSize // 更新起始点
// 读取 Blob 切片
reader.readAsArrayBuffer(chunkBlob)
reader.onload = () => {
// 累加已下载的字节数
// downloaded += reader.result.byteLength
if (typeof reader.result === 'string' || reader.result === null) {
console.log('reader.result不为 ArrayBuffer')
// 处理字符串的情况,可能需要转换为 ArrayBuffer 或者其他处理逻辑
} else {
downloaded += reader.result.byteLength
}
// 计算下载进度百分比
const total = blob.size
const progress = ((downloaded / total) * 100).toFixed(2)
config?.cbpercentage && config?.cbpercentage(progress) // 如果传递了获取下载进度的回调函数,则调用回调函数传递进度百分比
console.log(`下载进度: ${progress}%`)
// 如果还有数据未读取,则继续读取
if (start < total) {
readBlobInChunks()
} else {
console.log('下载完成.')
// 处理下载完成的数据,例如将其保存或显示
downFileFn(blob, _fileName)
// resolve(true)
}
}
}
readBlobInChunks()
})
.catch((error) => {
console.error('下载文件时出错:', error)
reject(error)
})
})
},
// 下载文件:3.0版本:优先使用传递的文件名,若不传递文件问就使用后端返回的文件名
downFileFetchV3: function (url: string, params?: Obj, config?: Config) {
let options: any = {
method: config?.method || 'GET'
}
let _headers
let _fileName = ''
if (options.method === 'GET' || options.method === 'get') {
_headers = {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
} else if (options.method === 'POST' || options.method === 'post') {
_headers = {
'Content-Type': 'application/json' // 设置为json格式
}
}
// 数组请求头: 优先使用传递的请求头,如果没有传递,则使用默认的请求头
options['headers'] = config?.headers || _headers
// 判断请求是否为get请求,若是get请求,则将params参数拼接在url后面
if (options.method === 'GET') {
const queryString = new URLSearchParams(params).toString()
url = url + '?' + queryString
}
// 处理post请求的参数
if ((options.method === 'post' || options.method === 'POST') && params) {
options['body'] = JSON.stringify(params)
}
// 定义每次读取的数据块大小(字节)
let chunkSize = config?.chunkSize || 10240 // 例如,10KB
// 初始化已下载的字节数
let downloaded = 0
// 初始化 Blob 切片起始点
let start = 0
return new Promise((resolve, reject) => {
fetch(url, options)
.then((res: any) => {
if (config?.fileName && res.ok) {
// 如果传递了文件名,则直接返回 Blob
_fileName = config?.fileName || '附件.xlsx'
return res.blob().then((blob: Blob) => ({ blob, _fileName }))
} else if (!config?.fileName && res.ok) {
// 如果没传递文件名,则使后端返回文件名
// 获取响应头中的 Content-Disposition(此属性中包含文件名)如:'attachment; filename="测试文件.xlsx"'
const contentDisposition = res.headers.get('Content-Disposition')
console.log('后端返回的文件名字符串: ', contentDisposition)
if (contentDisposition) {
// 解析文件名
const fileNameMatch =
contentDisposition.match(/filename="(.+)"/) || // attachment; filename="文件名.xlsx" 匹配文件名带双引号
contentDisposition.match(/filename='(.+)'/) || // attachment; filename='文件名.xlsx' 匹配文件名带单引号
contentDisposition.match(/filename=(?:([^;]+))/) || // attachment; filename=文件名.xlsx 匹配文件名不带引号
contentDisposition.match(/filename\*=utf-8''(.+)/) || // attachment; filename*=utf-8''文件名.xlsx
contentDisposition.match(/filename=(?:"([^"]+)"|'([^']+)'|([^;]+))/) //匹配文件名带双引号、单引号、不带引号
// console.log('文件名数组: ', fileNameMatch)
if (fileNameMatch.length > 1) {
// 匹配到文件名,则将文件名解码并赋值给 _fileName
_fileName = decodeURIComponent(
fileNameMatch[1] || fileNameMatch[2] || fileNameMatch[3]
)
console.log('后端返回的文件名: ', _fileName)
return res.blob().then((blob: Blob) => ({ blob, _fileName }))
}
}
}
if (!res.ok) {
console.log('res: ', res)
throw new Error('网络响应不正常!或其他错误')
}
// 如果没有找到文件名,直接返回 Blob
return res.blob().then((blob: Blob) => ({ blob, _fileName }))
})
.then(({ blob, _fileName }) => {
// console.log('blob: ', blob, _fileName)
const reader = new FileReader() // 创建 FileReader 对象
const readBlobInChunks = () => {
// 创建一个 Blob 切片,大小为 chunkSize
const chunkBlob = blob.slice(start, start + chunkSize)
start += chunkSize // 更新起始点
// 读取 Blob 切片
reader.readAsArrayBuffer(chunkBlob)
reader.onload = () => {
// 累加已下载的字节数
// downloaded += reader.result.byteLength
if (typeof reader.result === 'string' || reader.result === null) {
console.log('reader.result不为 ArrayBuffer')
// 处理字符串的情况,可能需要转换为 ArrayBuffer 或者其他处理逻辑
} else {
downloaded += reader.result.byteLength
}
// 计算下载进度百分比
const total = blob.size
const progress = ((downloaded / total) * 100).toFixed(2)
config?.cbpercentage && config?.cbpercentage(progress) // 如果传递了获取下载进度的回调函数,则调用回调函数传递进度百分比
console.log(`下载进度: ${progress}%`)
// 如果还有数据未读取,则继续读取
if (start < total) {
readBlobInChunks()
} else {
console.log('下载完成.')
// 处理下载完成的数据,例如将其保存或显示
downFileFn(blob, _fileName, resolve, reject)
// resolve(true)
}
}
}
readBlobInChunks()
})
.catch((error) => {
console.error('下载文件时出错:', error)
reject(error)
})
})
}
/*
sendJSONP: function (url, callbackName, callback) {
const script = document.createElement('script');
// 设置script元素的src属性,拼接callback参数
script.src = `${url}?callback=${callbackName}`;
// 将script元素添加到页面中
document.body.appendChild(script);
// 定义全局的回调函数,用于接收JSONP响应
window[callbackName] = function (data) {
// 执行传入的回调函数,并将JSONP响应作为参数传递
callback(data);
// 执行完毕后移除script元素
document.body.removeChild(script);
// 删除全局的回调函数
delete window[callbackName];
};
},
*/
}
export default ajaxBox
/**
* 使用下载示例 get
const exportFile = () => {
let rowData = InventoryTableRef.value!.getSelectionRows()
const url = storageModule + 'storage_standing_book_batch_export_get/'
const headers = {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
}
const params = {
ids: JSON.stringify(rowData.map((v: { id: any }) => v.id)),
state: 1,
org_id: UserData.orgId,
storage_type: 'CAILIAO',
}
CiPlus.Fn.ajaxBox.downFileFetch(
url,
'材料库_库存台账数据.xlsx',
'GET',
headers,
params,
)
}
使用下载示例 post
const exportFile = () => {
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
}) // 打开加载中
let rowData = WeighingTableRef.value!.getSelectionRows()
const url = storageModule + 'weighing_record_batch_export_post/'
const headers = {
'Content-Type': 'application/json', //设置为json格式
}
const params = {
ids: rowData.map((v: { id: any }) => v.id), // 直接数组形式
state: 1,
org_id: UserData.orgId,
}
console.log('params', params)
CiPlus.Fn.ajaxBox.downFileFetch(
url,
'称重记录数据(批次).xlsx',
'POST',
headers,
params,
).then((result) => {
console.log('result: ', result)
})
.catch((err) => {
console.log('err: ', err)
})
.finally(() => {
loading.close() // 关闭加载中
})
}
// 版本3.0:提供了.then和.catch方法,可以更方便地处理异步操作的结果和错误。
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
const url = CrossAuditModule + 'generate_report_get/'
const params = {
ids: ids,
}
ajaxBox
.downFileFetchV3(url, params, {
method: 'POST',
})
.then((res) => {
console.log('成功: ', res)
})
.catch((err) => {
console.log('失败: ', err)
}).finally(() => {
loading.close() // 关闭加载中
})
*/