当前位置: 首页 > 工具软件 > xlsx.js > 使用案例 >

JS操作excel文件

艾跃
2023-12-01

操作excel文件

1. 读取excel文件

a. 安装依赖

npm i xlsx -D

b. 代码演示(react 版)

import { Button, message, Tag, Tooltip, Upload } from 'antd';
import { UploadChangeParam, UploadFile, UploadProps } from 'antd/lib/upload/interface';
import React, { useCallback, useState } from 'react';
import XLSX from 'xlsx';
import { UploadOutlined } from '@ant-design/icons';

// 格式化内存
export function formatMemory(num: string | number) {
    if (/k|g|m|t/i.test(num.toString())) {
        return num.toString();
    }
    if (num == null) {
        return '';
    }
    const level = [ 'B', 'KB', 'MB', 'GB', 'TB' ];
    let cur = parseFloat(num.toString());
    let levelI = 0;
    while (cur >= 1024) {
        levelI++;
        cur /= 1024;
    }
    return `${Math.round(cur * 100) / 100} ${level[levelI]}`;
}


export function parseConcatRegionFile(file?: File | null, cb?: (data: any[]) => void) {
    if (file == null) {
        return null;
    }
    const reader = new FileReader();
    /** 这里判断下是否为csv文档 */

    let isCsv = file.name.endsWith('.csv');

    reader.onload = e => {
        /* Parse data */
        let bstr = e.target?.result;
        if (bstr == null) {
            cb && cb([]);
            return;
        }
        if (isCsv) {
            const uni8Array = new Uint8Array(bstr as ArrayBuffer);
            /** 239,187,191 === \ufeff */
            const coding = uni8Array[0] === 239 && uni8Array[1] === 187 && uni8Array[2] === 191 ? 'utf8' : 'gbk';
            const t = new TextDecoder(coding);
            bstr = t.decode(uni8Array);
        }
        const wb = XLSX.read(bstr, { type: isCsv ? 'string' : 'array' });

        /* Get first worksheet */
        const wsname = wb.SheetNames[0];
        const ws = wb.Sheets[wsname];
        /* Convert array of arrays */
        const data = XLSX.utils.sheet_to_json<string[]>(ws, { header: 1 });

        if (data == null || data.length <= 1) {
            cb && cb([]);
            return;
        }
        cb && cb(data);
    };

    reader.readAsArrayBuffer(file as any);

    return () => {
        reader.abort();
    };
}

interface UploadImgProps extends Omit<UploadProps, 'onChange'> {
    uploadNode?: React.ReactNode;
    maxSize?: number; // 最大上传size,单位为b, 默认2M
    valueMaxlength?: number; // 文件名显示最大长度
    onChange?: (data: any[]) => void; // 输出识别到的数组,原样输出
}

function ReadFile(props: UploadImgProps) {
    const { maxSize = 2 * 1024 * 1024, accept, valueMaxlength = 20, uploadNode, ...otherReset } = props;

    const [ value, setValue ] = useState('');

    const beforeUpload = useCallback(
        file => {
            console.log(accept, accept?.toLowerCase().split(','), file.type);
            if (
                accept != null &&
                !accept?.toLowerCase().split(',').includes(file.type) &&
                !accept
                    .toLowerCase()
                    .split(',')
                    .some(name => file?.name.endsWith(name))
            ) {
                message.error(`文件扩展名 ${file.type} 错误,需要 ${accept}`);
                return false;
            }
            if (maxSize && file.size > maxSize) {
                message.error('文件大小最大不能超过' + formatMemory(maxSize));
                return false;
            }
            return true;
        },
        [ accept, maxSize ]
    );

    const handleChange = useCallback(
        (info: UploadChangeParam<UploadFile<any>>) => {
            const { file } = info;
            setValue(file?.name);
            parseConcatRegionFile(file?.originFileObj as File, data => {
                props?.onChange?.(data || []);
            });
        },
        [ props ]
    );

    const handleClose = useCallback(() => {
        setValue('');
        props?.onChange?.([]);
    }, [ props ]);

    return (
        <>
            {value ? (
                <Tooltip title={value}>
                    <Tag closable onClose={handleClose}>
                        {value?.length > valueMaxlength ? value?.substring(0, valueMaxlength) + '...' : value}
                    </Tag>
                </Tooltip>
            ) : (
                <Upload
                    {...otherReset}
                    accept={accept || 'application/vnd.ms-excel'}
                    beforeUpload={beforeUpload}
                    onChange={handleChange}
                >
                    {uploadNode || <Button icon={<UploadOutlined />}>数据导入</Button>}
                </Upload>
            )}
        </>
    );
}
export default ReadFile;

2. excel 导出

a. 后端response返回文件流导出

export function createBlob(data: any, filename: string, blobOptions: BlobPropertyBag) {
    let blob = new Blob([ data ], blobOptions || { type: 'application/vnd.ms-excel' });
    let dom = document.createElement('a');
    dom.download = filename;
    dom.style.display = 'none';
    dom.href = URL.createObjectURL(blob);
    document.body.appendChild(dom);
    dom.click();
    setTimeout(() => {
        document.body.removeChild(dom);
    }, 1000);
}

// 调用demo演示
axios.get(`/api/demo`, { params, responseType: 'blob' }).then(data => {
     createBlob(data, `xxxxx.csv`);
})

b. JSON 数据转文件导出

i. 使用浏览器自带的href属性导出文件

export function jsonToExcel(jsonData: any[], filename = 'export.xls') {
    if (!Array.isArray(jsonData) || !jsonData?.length) {
        return;
    }
    let str = '';
    // 列标题
    Object.keys(jsonData[0]).forEach(k => {
        str += k + '\t,';
    });
    str += '\n';
    // 增加\t为了不让表格显示科学计数法或者其他格式
    for (let i = 0; i < jsonData.length; i++) {
        // eslint-disable-next-line no-loop-func
        Object.keys(jsonData[i]).forEach(key => {
            str += `${jsonData[i][key] + '\t'},`;
        });
        str += '\n';
    }
    // encodeURIComponent解决中文乱码
    const uri = `data:text/${filename.split('.').pop()};charset=utf-8,\ufeff${encodeURIComponent(str)}`;
    // 通过创建a标签实现
    let dom = document.createElement('a');
    dom.download = filename;
    dom.style.display = 'none';
    dom.href = uri;
    document.body.appendChild(dom);
    dom.click();
    setTimeout(() => {
        document.body.removeChild(dom);
    }, 1000);
}

// demo调用演示
let sheet1data = [ // 注意数组中每列key的排序,因为导出列表时按照key的排序导出的
    { 部门: '行政部', 姓名: 'zhangsan', age: 18 },
    { 部门: 'IT', 姓名: 'lisi', age: 19 }
];
jsonToExcel(
    sheet1data,
    'demo.xls'
);

ii. 使用xlsx write导出

/** json转excel表格 */
export function jsonToExcel(data: any[], filename: string) {
    let wopts: any = {
        bookType: filename.split('.').pop(),
        bookSST: false,
        type: 'binary'
    };
    /* create a new blank workbook */
    let workbook = XLSX.utils.book_new();
    let sheet1 = XLSX.utils.json_to_sheet(data);
    XLSX.utils.book_append_sheet(workbook, sheet1, 'sheet1');
    let wbout = XLSX.write(workbook, wopts);
    createBlob([ string2u8buff(wbout) ], filename);
}

function string2u8buff(s: string) {
    let buf = new ArrayBuffer(s.length);
    let view = new Uint8Array(buf);
    for (let i = 0; i !== s.length; ++i) {
        view[i] = s.charCodeAt(i) & 0xff;
    }
    return buf;
}

// demo调用演示
let sheet1data = [ // 注意数组中每列key的排序,因为导出列表时按照key的排序导出的
    { 部门: '行政部', 姓名: 'zhangsan', age: 18 },
    { 部门: 'IT', 姓名: 'lisi', age: 19 }
];
jsonToExcel(
    sheet1data,
    'demo.xls'
);
 类似资料: