admin-vben/apps/web-antd/src/utils/download.ts

216 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 下载工具模块
* 提供多种文件格式的下载功能
*/
// 请使用 @vben/utils/download 代替 packages/@core/base/shared/src/utils/download.ts
/**
* 图片下载配置接口
*/
interface ImageDownloadOptions {
/** 图片 URL */
url: string;
/** 指定画布宽度 */
canvasWidth?: number;
/** 指定画布高度 */
canvasHeight?: number;
/** 将图片绘制在画布上时带上图片的宽高值,默认为 true */
drawWithImageSize?: boolean;
}
/**
* 基础文件下载函数
* @param data - 文件数据 Blob
* @param fileName - 文件名
* @param mimeType - MIME 类型
*/
export const download0 = (data: Blob, fileName: string, mimeType: string) => {
try {
// 创建 blob
const blob = new Blob([data], { type: mimeType });
// 创建 href 超链接,点击进行下载
window.URL = window.URL || window.webkitURL;
const href = URL.createObjectURL(blob);
const downA = document.createElement('a');
downA.href = href;
downA.download = fileName;
downA.click();
// 销毁超链接
window.URL.revokeObjectURL(href);
} catch (error) {
console.error('文件下载失败:', error);
throw new Error(
`文件下载失败: ${error instanceof Error ? error.message : '未知错误'}`,
);
}
};
/**
* 触发文件下载的通用方法
* @param url - 下载链接
* @param fileName - 文件名
*/
const triggerDownload = (url: string, fileName: string) => {
const a = document.createElement('a');
a.href = url;
a.download = fileName;
a.click();
};
export const download = {
/**
* 下载 Excel 文件
* @param data - 文件数据 Blob
* @param fileName - 文件名
*/
excel: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/vnd.ms-excel');
},
/**
* 下载 Word 文件
* @param data - 文件数据 Blob
* @param fileName - 文件名
*/
word: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/msword');
},
/**
* 下载 Zip 文件
* @param data - 文件数据 Blob
* @param fileName - 文件名
*/
zip: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/zip');
},
/**
* 下载 HTML 文件
* @param data - 文件数据 Blob
* @param fileName - 文件名
*/
html: (data: Blob, fileName: string) => {
download0(data, fileName, 'text/html');
},
/**
* 下载 Markdown 文件
* @param data - 文件数据 Blob
* @param fileName - 文件名
*/
markdown: (data: Blob, fileName: string) => {
download0(data, fileName, 'text/markdown');
},
/**
* 下载 JSON 文件
* @param data - 文件数据 Blob
* @param fileName - 文件名
*/
json: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/json');
},
/**
* 下载图片(允许跨域)
* @param options - 图片下载配置
*/
image: (options: ImageDownloadOptions) => {
const {
url,
canvasWidth,
canvasHeight,
drawWithImageSize = true,
} = options;
const image = new Image();
// image.setAttribute('crossOrigin', 'anonymous')
image.src = url;
image.addEventListener('load', () => {
try {
const canvas = document.createElement('canvas');
canvas.width = canvasWidth || image.width;
canvas.height = canvasHeight || image.height;
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
ctx?.clearRect(0, 0, canvas.width, canvas.height);
if (drawWithImageSize) {
ctx.drawImage(image, 0, 0, image.width, image.height);
} else {
ctx.drawImage(image, 0, 0);
}
const dataUrl = canvas.toDataURL('image/png');
triggerDownload(dataUrl, 'image.png');
} catch (error) {
console.error('图片下载失败:', error);
throw new Error(
`图片下载失败: ${error instanceof Error ? error.message : '未知错误'}`,
);
}
});
image.addEventListener('error', () => {
throw new Error('图片加载失败');
});
},
/**
* 将 Base64 字符串转换为文件对象
* @param base64 - Base64 字符串
* @param fileName - 文件名
* @returns File 对象
*/
base64ToFile: (base64: string, fileName: string): File => {
// 输入验证
if (!base64 || typeof base64 !== 'string') {
throw new Error('base64 参数必须是非空字符串');
}
// 将 base64 按照逗号进行分割,将前缀与后续内容分隔开
const data = base64.split(',');
if (data.length !== 2 || !data[0] || !data[1]) {
throw new Error('无效的 base64 格式');
}
// 利用正则表达式从前缀中获取类型信息image/png、image/jpeg、image/webp等
const typeMatch = data[0].match(/:(.*?);/);
if (!typeMatch || !typeMatch[1]) {
throw new Error('无法解析 base64 类型信息');
}
const type = typeMatch[1];
// 从类型信息中获取具体的文件格式后缀png、jpeg、webp
const typeParts = type.split('/');
if (typeParts.length !== 2 || !typeParts[1]) {
throw new Error('无效的 MIME 类型格式');
}
const suffix = typeParts[1];
try {
// 使用 atob() 对 base64 数据进行解码,结果是一个文件数据流以字符串的格式输出
const bstr = window.atob(data[1]);
// 获取解码结果字符串的长度
const n = bstr.length;
// 根据解码结果字符串的长度创建一个等长的整型数字数组
const u8arr = new Uint8Array(n);
// 优化的 Uint8Array 填充逻辑
for (let i = 0; i < n; i++) {
// 使用 charCodeAt() 获取字符对应的字节值Base64 解码后的字符串是字节级别的)
// eslint-disable-next-line unicorn/prefer-code-point
u8arr[i] = bstr.charCodeAt(i);
}
// 返回 File 文件对象
return new File([u8arr], `${fileName}.${suffix}`, { type });
} catch (error) {
throw new Error(
`Base64 解码失败: ${error instanceof Error ? error.message : '未知错误'}`,
);
}
},
};