解析 CSV 文件, 并解决对应中文编码问题
正确配置 encoding 值,即可解决文件编码问题
import * as jschardet from "jschardet";
import { parse } from "papaparse";
/**
* 检查编码
* @param base64Str Base64 字符串
* @returns 编码类型
*/
const detectEncoding = (base64Str: string): string => {
const str = atob(base64Str.split(";base64,")[1]);
let encoding = jschardet.detect(str).encoding;
if (encoding === "windows-1252") {
encoding = "ANSI";
}
return encoding;
};
/**
* 解析 CSV 文件
* @param file CSV 文件
* @returns 解析后的二维数组
*/
export const parseCsvFile = (file: File): Promise<any[][]> => {
return new Promise((resolve, reject) => {
const fReader = new FileReader();
fReader.readAsDataURL(file);
fReader.onload = (evt: ProgressEvent<FileReader>) => {
if (evt.target?.result) {
const data = evt.target.result as string;
const encoding = detectEncoding(data);
parse(file, {
encoding: encoding,
complete: (results: any) => {
let res = results.data;
if (res[res.length - 1] === "") {
res.pop();
}
console.log("res", res);
resolve(res);
},
error: (error: any) => {
reject(error);
},
});
} else {
reject(new Error("文件读取失败"));
}
};
fReader.onerror = (error) => {
reject(error);
};
});
};
使用
import { Category } from "@/services/types/aiVideoCreation";
import { UploadOutlined } from "@ant-design/icons";
import { Button, Modal, Table, Upload } from "antd";
import React, { useEffect, useMemo, useState } from "react";
import { parseCsvFile } from "./tool";
import "./index.less";
interface CsvUploadModalProps {
visible: boolean;
categories: Category[];
onOk: (data: any[]) => void;
onCancel: () => void;
}
const CsvUploadModal: React.FC<CsvUploadModalProps> = ({
visible,
onOk,
onCancel,
categories,
}) => {
const [csvData, setCsvData] = useState<any[]>([]);
useEffect(() => {
visible && setCsvData([]);
}, [visible]);
const handleUpload = (info: any) => {
parseCsvFile(info.file)
.then((res: any) => {
console.log("我的数据", res);
const parsedData = res
?.slice(1)
?.map((row: any) => {
const [
desc,
category_name,
sub_category_name,
expect_num,
cp_id,
sync_title,
folder_id,
urls,
] = row;
return {
desc,
category_name,
category2_name: sub_category_name,
category_id: getCategoryIdByName(category_name) || "",
category2_id:
getCategoryIdByName(category_name, sub_category_name) || "",
expect_num: Number(expect_num),
cp_id: Number(cp_id) || 0,
sync_title: sync_title || "",
folder_id,
urls: urls?.split("\n") || [],
};
});
setCsvData(parsedData);
})
.catch((error) => {
console.error("解析 CSV 文件时出错:", error);
});
};
const columns = [
{
title: "任务描述",
dataIndex: "desc",
key: "desc",
width: 250,
render: (desc: string) => (
<div>
{desc?.split("\n")?.map((el: string, index: number) => (
<div key={index}>{el}</div>
))}
</div>
),
},
{
title: "一级分类",
dataIndex: "category_name",
key: "category_name",
},
{
title: "二级分类",
dataIndex: "category2_name",
key: "category2_name",
},
{
title: "建议生成条数",
dataIndex: "expect_num",
key: "expect_num",
},
{
title: "发布账号 ID",
dataIndex: "cp_id",
key: "cp_id",
},
{
title: "同步标题",
dataIndex: "sync_title",
key: "sync_title",
width: 200,
},
{
title: "同步到视频库的位置",
dataIndex: "folder_id",
key: "folder_id",
},
{
title: "素材地址(可多链接,换行有效)",
dataIndex: "urls",
key: "urls",
width: 300,
render: (urls: string[]) => (
<div className="urls-container">
{urls.map((url, index) => (
<div>
<a
key={index}
href={url}
target="_blank"
rel="noopener noreferrer"
>
{url}
</a>
</div>
))}
</div>
),
},
];
const dataSource = useMemo(() => {
return csvData.map((item, index) => ({
key: index,
...item,
}));
}, [csvData]);
return (
<Modal
title="上传 CSV 文件"
visible={visible}
centered
className="csv-upload-modal"
width="calc(100vw - 200px)"
onCancel={onCancel}
footer={
<div className="csv-upload__btns">
<Button
key="download-template"
type="link"
href={""}
>
下载 CSV 模版
</Button>
<div>
<Button key="cancel" onClick={onCancel}>
取消
</Button>
<Button key="submit" type="primary" onClick={() => onOk(csvData)}>
确认
</Button>
</div>
</div>
}
>
{csvData.length > 0 ? (
<Table
columns={columns}
dataSource={dataSource}
pagination={false}
style={{ marginTop: 20, width: "100%" }}
scroll={{ y: 300 }} // 固定表格高度
/>
) : (
<Upload
accept=".csv"
showUploadList={false}
beforeUpload={() => false}
onChange={handleUpload}
className="csv-upload__wrapper"
>
<Button icon={<UploadOutlined />}>
点击上传 CSV 文件
</Button>
</Upload>
)}
</Modal>
);
};
export default CsvUploadModal;