Nest 实现 excel 导入导出
安装依赖
bash
npm install --save exceljs创建 excel.service 和 excel.module
excel.module.ts
ts
import { DynamicModule, Module } from "@nestjs/common";
import { ExcelService } from "./excel.service";
export interface DataOptions {
name: string;
}
@Module({})
export class ExcelModule {
static forRoot(options: DataOptions): DynamicModule {
return {
module: ExcelModule,
controllers: [],
providers: [
ExcelService,
{
provide: "EXCEL_OPTIONS",
useValue: options,
},
],
exports: [ExcelService],
};
}
}excel.service.ts
ts
import { Inject, Injectable } from "@nestjs/common";
import { ExcelModule, type DataOptions } from "./excel.module";
import { Workbook, Cell } from "exceljs";
import { GlobalCheckException } from "../../common/globalcheck.exception";
@Injectable()
export class ExcelService {
@Inject("EXCEL_OPTIONS")
private readonly options: DataOptions;
// 导入 获取到每个sheet页的数据
async importexcel(buffer: any) {
const workbook = new Workbook();
const workbook2 = await workbook.xlsx.load(buffer);
const resultAll: any = [];
workbook2.eachSheet((worksheet, index) => {
const resultsheet: any[] = [];
worksheet.eachRow((row, rowNumber) => {
const rowData: any = [];
row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
// 检查是否为 null 或空字符串
if (
cell.value == null ||
(typeof cell.value === "string" && cell.value.trim() === "")
) {
throw new GlobalCheckException(
`第${rowNumber}行第${colNumber}列数据为空`
);
}
rowData.push(cell.value);
});
resultsheet.push(rowData);
});
resultAll.push(resultsheet);
});
return resultAll;
}
// 导出
async exportExcel1() {
const workbook = new Workbook();
const worksheet = workbook.addWorksheet("第一个sheet");
worksheet.columns = [
{ header: "ID", key: "id", width: 20 },
{ header: "姓名", key: "name", width: 30 },
{ header: "出生日期", key: "birthday", width: 30 },
{ header: "手机号", key: "phone", width: 50 },
];
const data = [
{
id: 1,
name: "光光",
birthday: new Date("1994-07-07"),
phone: "13255555555",
},
{
id: 2,
name: "东东",
birthday: new Date("1994-04-14"),
phone: "13222222222",
},
{
id: 3,
name: null,
birthday: new Date("1995-08-08"),
phone: "13211111111",
},
];
worksheet.addRows(data);
// 第一种写入,然后再让前端调用下载接口下载 这种情况服务器会存储文件
workbook.xlsx.writeFile(process.cwd() + "/public/data2.xlsx");
// 返回路径
return "http://localhost:5000/static/data2.xlsx";
}
// 工具类
async exportExcelTools(colunms: any, data: unknown[], sheetName: string) {
const workBook = new Workbook();
const sheet = workBook.addWorksheet(sheetName);
sheet.columns = colunms;
data.forEach((row) => {
sheet.addRow(row);
});
console.log(data);
return await workBook.xlsx.writeBuffer();
}
// 第2种导出 直接下载
async exportExcel2() {
const sheetName = "sheet";
const columns = [
{ header: "ID", key: "id", width: 20 },
{ header: "姓名", key: "name", width: 30 },
{ header: "出生日期", key: "birthday", width: 30 },
{ header: "手机号", key: "phone", width: 50 },
];
const data = [
{
id: 1,
name: "光光44",
birthday: new Date("1994-07-07"),
phone: "13255555555",
},
{
id: 2,
name: "东东5454",
birthday: new Date("1994-04-14"),
phone: "13222222222",
},
{
id: 3,
name: "小刚6565",
birthday: new Date("1995-08-08"),
phone: "13211111111",
},
];
const buffer = await this.exportExcelTools(columns, data, sheetName);
return buffer;
}
}其他模块 使用
其他模块模型
ts
import { Module } from "@nestjs/common";
import { TestdemoService } from "./testdemo.service";
import { TestdemoController } from "./testdemo.controller";
// 增加Excel 动态模块
import { ExcelModule } from "../../commonModules/excel/excel.module";
@Module({
imports: [ExcelModule.forRoot({ name: "testdemoexcel" })],
controllers: [
TestdemoController, // Excel 动态模块
],
providers: [TestdemoService],
})
export class TestdemoModule {}控制器
ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
UseGuards,
Req,
Res,
} from "@nestjs/common";
import { TestdemoService } from "./testdemo.service";
import { AuthGuard } from "@nestjs/passport";
// 通过这个装饰器让全局拦截器放行
import { Bypass } from "../../common/decorators/bypass.decorator";
import { memoryStorage } from "multer";
@Controller({
path: "testdemo",
version: ["1"],
})
export class TestdemoController {
constructor(private readonly testdemoService: TestdemoService) {}
// 导入Excel 测试
@Post("importexcel1")
@ByGuardpass()
@UseInterceptors(
// 独立自己的
FileInterceptor("file", {
storage: memoryStorage(),
limits: {
fileSize: 5 * 1024 * 1024, // 限制文件大小为 5MB,防止过大
},
})
)
async importexcel1(
@UploadedFile()
file: Express.Multer.File
) {
let result = await this.testdemoService.importexcel(file.buffer);
return {
data: result,
};
}
// 第一种导出
@Get("exportexcel1")
async exportexcel1() {
// 这里应该上传成功后,返回一个url,然后通过url获取到excel文件
const result = await this.testdemoService.exportexcel1();
console.log(result);
return {
data: result,
};
}
// 第2种导出
@Get("exportexcel2")
@Bypass()
async exportexcel2(@Res() res: any) {
// 这里应该上传成功后,返回一个url,然后通过url获取到excel文件
const result = await this.testdemoService.exportexcel2();
console.log(result);
return res.send(result);
}
}服务
- testdemo.service.ts
ts
import { Inject, Injectable } from "@nestjs/common";
import { ExcelService } from "../../commonModules/excel/excel.service";
@Injectable()
export class TestdemoService {
// Excel
@Inject()
private readonly ExcelService: ExcelService;
// Excel导入
async importexcel(data) {
const result = await this.ExcelService.importexcel(data);
return result;
}
// 第一种Excel导出
async exportexcel1() {
const result = await this.ExcelService.exportExcel1();
return {
data: result,
};
}
// 第2种Excel导出
async exportexcel2() {
const result = await this.ExcelService.exportExcel2();
return result;
}
}对应的前端页面
导入
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片上传</title>
</head>
<body>
<form id="uploadForm" enctype="multipart/form-data">
<label for="excelInput">excel:</label>
<input type="file" id="excelInput" name="image" accept="file/*" />
<button type="button" onclick="uploadImage()">导入</button>
</form>
<script>
function uploadImage() {
const fileInput = document.getElementById("excelInput");
const file = fileInput.files[0]; // 获取选中的文件
if (!file) {
alert("请选择一个文件");
return;
}
const formData = new FormData();
formData.append("file", file); // 'image' 是后端期待的字段名,根据需要调整
fetch("http://localhost:5000/v1/testdemo/importexcel1", {
// '/upload' 是你的后端处理上传的URL,需要替换为实际URL
method: "POST",
body: formData,
})
.then((response) => response.json()) // 假设服务器返回JSON格式的响应
.then((data) => {
console.log("Success:", data); // 处理响应数据,例如显示上传成功信息等
if (data.code == 200) {
alert("上传成功");
} else {
alert("上传失败");
}
})
.catch((error) => {
console.error("Error:", error); // 处理错误情况,例如显示错误信息等
alert("上传失败2222"); // 或者在页面上显示错误消息等操作
});
}
</script>
</body>
</html>导出
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/axios@1.5.0/dist/axios.min.js"></script>
</head>
<body>
<button
onClick="downloadBlob('http://localhost:5000/excel/exportExcel2','测试.xlsx')"
>
下载Blob
</button>
<button
onClick="downlandlink('http://localhost:5000/static/data2.xlsx','测试.xlsx')"
>
下载连接
</button>
</body>
<script>
async function downloadBlob(url, filename) {
try {
// 发送 GET 请求并设置 responseType 为 'blob'
const response = await axios.get(url, {
responseType: "blob",
});
// 创建一个临时 URL 用于下载
const blobUrl = window.URL.createObjectURL(new Blob([response.data]));
// 创建一个隐藏的 a 标签并触发点击
const link = document.createElement("a");
link.href = blobUrl;
link.setAttribute("download", filename);
document.body.appendChild(link);
link.click();
// 清理临时 URL 和 a 标签
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
} catch (error) {
console.error("下载失败:", error);
}
}
// 下载连接
async function downlandlink(url, filename) {
try {
// 发送 GET 请求并设置 responseType 为 'blob'
const response = await axios.get(url, {
responseType: "blob",
});
// 创建一个临时 URL 用于下载
const blobUrl = window.URL.createObjectURL(new Blob([response.data]));
// 创建一个隐藏的 a 标签并触发点击
const link = document.createElement("a");
link.href = blobUrl;
link.setAttribute("download", filename);
document.body.appendChild(link);
link.click();
// 清理临时 URL 和 a 标签
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
} catch (error) {
console.error("下载失败:", error);
}
}
</script>
</html>