Nodejs 里Excel开发实践
发布于 5 年前 作者 i5ting 7159 次浏览 最后一次编辑是 3 年前 来自 分享

excel的导入导出在一般业务系统里是非常常见的,但是nodejs里并没有成熟的处理excel的库,导入问题不大,稍微复杂的导出就搞不定了

那么,只剩下曲线救国一条路了

excel的导入导出在一般业务系统里是非常常见的,但是nodejs里并没有成熟的处理excel的库,导入问题不大,稍微复杂的导出就搞不定了

  • exceljs
  • excel.js
  • node-xlsx

都太简单了…

那么,只剩下曲线救国一条路了

合理架构

node的优点就不说了,它虽好,但毕竟才出来几年,很多东西都不够完善的,所以在使用这种比较新比较潮的技术的时候,一定要学会扬长避短,不然会很难受

apache的poi是一个不错的库,非常成熟,我从07年就开始用它,

下面是百科的简介

Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“可怜的模糊实现”。

Apache POI 是创建和维护操作各种符合Office Open XML(OOXML)标准和微软的OLE 2复合文档格式(OLE2)的Java API。用它可以使用Java读取和创建,修改MS Excel文件.而且,还可以使用Java读取和创建MS Word和MSPowerPoint文件。Apache POI 提供Java操作Excel解决方案(适用于Excel97-2008)。

http://poi.apache.org/

我们需要考虑以下问题:

  • 多语言是否合适?
  • 分拆功能是否合适?

思考

系统小的时候肯定会尽量统一的,系统大了就会有各种鸟了

比如创业初期,可能是rails or express写的东西,但随着业务的壮大,会发现瓶颈的,这个时候你需要扩容

你多加台机器,然后部署一次

可是这样真的好么?

理想的做法是拆分成小模块,就像处理redis缓存一样,业务量大了,就立马多起几个docker实例,非常好运维

而且从职责上看,它也是小而美的代表

既然小了,那么你用什么语言开发还重要么?

比如我的核心系统是expressjs写的,我的下载就用的java写的,我部署了2个,然后nginx处理一下,谁又能看出来差别呢?

而且我的express一定要多起几个实例,而java的可能并发比较少,我没有去浪费资源。

分享一个简单的工具类

package im.xbm.dlcenter.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ExcelUtils {
	// 对外提供读取excel文件的接口
	public static List<List<Object>> readExcel(File file) throws IOException {
		String fName = file.getName();
		String extension = fName.lastIndexOf(".") == -1 ? "" : fName
				.substring(fName.lastIndexOf(".") + 1);
		if ("xls".equals(extension)) {// 2003
			System.err.println("读取excel2003文件内容");
			return read2003Excel(file);
		} else if ("xlsx".equals(extension)) {// 2007
			System.err.println("读取excel2007文件内容");
			return read2007Excel(file);
		} else {
			throw new IOException("不支持的文件类型:" + extension);
		}
	}

	/**
	 * 读取2003excel
	 * 
	 * @param file
	 * @return
	 */
	private static List<List<Object>> read2003Excel(File file)
			throws IOException {
		List<List<Object>> dataList = new ArrayList();
		HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(file));
		HSSFSheet sheet = wb.getSheetAt(0);
		HSSFRow row = null;
		HSSFCell cell = null;
		Object val = null;
		DecimalFormat df = new DecimalFormat("0");// 格式化数字
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化日期字符串
		for (int i = sheet.getFirstRowNum(); i < sheet
				.getPhysicalNumberOfRows(); i++) {
			row = sheet.getRow(i);
			if (row == null) {
				continue;
			}
			List<Object> objList = new ArrayList<Object>();
			for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
				cell = row.getCell(j);
				if (cell == null) {
					val = null;
					objList.add(val);
					continue;
				}
				switch (cell.getCellType()) {
				case HSSFCell.CELL_TYPE_STRING:
					val = cell.getStringCellValue();
					break;
				case HSSFCell.CELL_TYPE_NUMERIC:
					if ("@".equals(cell.getCellStyle().getDataFormatString())) {
						val = df.format(cell.getNumericCellValue());
					} else if ("General".equals(cell.getCellStyle()
							.getDataFormatString())) {
						val = df.format(cell.getNumericCellValue());
					} else {
						val = sdf.format(HSSFDateUtil.getJavaDate(cell
								.getNumericCellValue()));
					}
					break;
				case HSSFCell.CELL_TYPE_BOOLEAN:
					val = cell.getBooleanCellValue();
					break;
				case HSSFCell.CELL_TYPE_BLANK:
					val = "";
					break;
				default:
					val = cell.toString();
					break;
				}
				objList.add(val);
			}
			dataList.add(objList);
		}
		return dataList;
	}

	/**
	 * 读取excel表头
	 * 
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public static String[] readExcelHead(File file) throws IOException {
		HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(file));
		HSSFSheet sheet = wb.getSheetAt(0);
		HSSFRow row = null;
		HSSFCell cell = null;
		row = sheet.getRow(0);
		String[] buff = new String[row.getLastCellNum()];
		for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
			cell = row.getCell(i);
			buff[i] = cell.getStringCellValue();
		}
		return buff;
	}

	/**
	 * 读取2007excel
	 * 
	 * @param file
	 * @return
	 */

	private static List<List<Object>> read2007Excel(File file)
			throws IOException {
		List<List<Object>> dataList = new ArrayList<List<Object>>();
		XSSFWorkbook xwb = new XSSFWorkbook(new FileInputStream(file));
		XSSFSheet sheet = xwb.getSheetAt(0);
		XSSFRow row = null;
		XSSFCell cell = null;
		Object val = null;
		DecimalFormat df = new DecimalFormat("0");// 格式化数字
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化日期字符串
		for (int i = sheet.getFirstRowNum(); i < sheet
				.getPhysicalNumberOfRows(); i++) {
			row = sheet.getRow(i);
			if (row == null) {
				continue;
			}
			List<Object> objList = new ArrayList<Object>();
			for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
				cell = row.getCell(j);
				if (cell == null) {
					val = null;
					objList.add(val);
					continue;
				}
				switch (cell.getCellType()) {
				case XSSFCell.CELL_TYPE_STRING:
					val = cell.getStringCellValue();
					break;
				case XSSFCell.CELL_TYPE_NUMERIC:
					if ("@".equals(cell.getCellStyle().getDataFormatString())) {
						val = df.format(cell.getNumericCellValue());
					} else if ("General".equals(cell.getCellStyle()
							.getDataFormatString())) {
						val = df.format(cell.getNumericCellValue());
					} else {
						val = sdf.format(HSSFDateUtil.getJavaDate(cell
								.getNumericCellValue()));
					}
					break;
				case XSSFCell.CELL_TYPE_BOOLEAN:
					val = cell.getBooleanCellValue();
					break;
				case XSSFCell.CELL_TYPE_BLANK:
					val = "";
					break;
				default:
					val = cell.toString();
					break;
				}
				objList.add(val);
			}
			dataList.add(objList);
		}
		return dataList;
	}
}

从这个可以看出poi的用法,整体还是比较简单

这是读取部分的代码,写的部分也很简单,涉及公司业务,就不放上来了

开发知识点

你需要学的是

  • 会基本的servlet,写接口(eclipse新建项目的时候,选择dynamic项目)
  • 使用eclipse调试开发
  • 使用mongodb java driver去操作mongodb
  • 使用poi去读取和生成文件
  • 使用java处理一下文件路径

这里就不科普了,javaEE的教程一搜一大堆

心态

不要排斥其他语言,用好它们的长处才是本事

现在是一个合作的时代

欢迎关注我的公众号【node全栈】 node全栈.png 原文

19 回复

友情提醒大家, Java 的

@russj 求教:复杂的excel导入导出,node如何做?我没有找到更好的办法

exceljs 和 excel.js 复杂的就不行了。。。。

我用的这个 https://www.npmjs.com/package/node-xlsx 可能我的还不够复杂

@russj excel 2007版本的咋办呢?

@magicdawn O(∩_∩)O谢谢啊

https://github.com/nodeonly/officegen/blob/master/examples/make_xlsx.js

excel可不只是数据的,还有样式等等,它并不支持,这个咋破?

你为什么不用C#版的

@coolicer 恩,NPOI也可以,不过部署服务器会比较麻烦

等C#跨平台了再考虑吧

@i5ting mono也是可以的吧。

@i5ting

搜了下,这个也是 ECMA规范,汗! http://www.ecma-international.org/publications/standards/Ecma-376.htm

so : 提Issue/提PR/换其他的

不如c#方便好用

@gotolnc 考虑部署么?

https://cnodejs.org/topic/54afa85cce87bace2444ce44 这个复杂的更好用 模板+数据=导出

@151263 你确定你试过了么?

@i5ting 1年前,做了系统,里面有30多张复杂的报表,赚了几十万了,你说呢

@i5ting 你提出的这些办法,我想问,假如要做非常复杂的财务报表,里面还有动态公式,还需要宏,还要柱状图,Excel本身还要只读状态,还要密码,等等等等,怎么搞?
然后客户还告诉你,这需求不是最终版的, 你先做, 做出来之后,他给老板看了才能确定对不对,可能还要再改多次, 我想说,用这种写代码来导出Excel,只能导出简单的, 在很多场合,远远不够

狼叔,现在node有好的导入导出功能没 还是node-xlsx吗

@dengnan123 在有office的情况下用wanx会舒服些

回到顶部