需求:
批量生成打印机使用的打印文件.
500条信息为一个文件, 超过500条则生成第二个文件, 将最终生成的文件以压缩包形式下载;
接到需求我的第一思路是先使用io流 在服务器生成服务器本地文件, 将所有文件生成好之后, 再讲文件进行压缩然后下载. 然后每天凌晨定时任务扫描文件夹进行文件清理.
后来公司的同事给提供了一个新的思路, 不用在本地生成文件, 而是使用内存流的形式代替, 因为生成的文件内存占用并不是很大.
controller 层
@GetMapping("downloadForStock")
@ApiOperationSupport(order = 12)
@ApiOperation(value = "批量下载for指令", notes = "传入货品ids")
public R downloadForStock(@RequestParam String ids, @ApiIgnore HttpServletResponse response) {
if (Func.isBlank(ids)) {
return R.fail("请选择正确的码数据进行下载");
}
List<GoodsEntity> goodsList = goodsService.list(Wrappers.<GoodsEntity>lambdaQuery().in(GoodsEntity::getId, Func.toLongList(ids)));
stockService.downloadForStockGoods(goodsList, response);
return R.status(true);
}
service层
@Override
@SneakyThrows
@Transactional(rollbackFor = Exception.class)
public void downloadForStockGoods(List<GoodsEntity> goodsList, HttpServletResponse response) {
if (Func.isEmpty(goodsList)) {
throw new ServiceException("打印失败,货物信息为空!");
}
// 创建文件流
Map<String, byte[]> fileMap = this.creatFile(goodsList);
//下载压缩包
ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("附件.zip", "UTF-8"));
// 创建 ZipEntry 对象
Set<String> keySet = fileMap.keySet();
for (String key : keySet) {
ZipEntry zipEntry = new ZipEntry(key);
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write(fileMap.get(key));
}
zipOutputStream.closeEntry();
zipOutputStream.close();
}
@SneakyThrows
private Map<String, byte[]> creatFile(List<GoodsEntity> goodsList) {
Map<String, byte[]> resMap = new HashMap();
Map<Long, GoodsEntity> goodsMap = goodsList.stream().collect(Collectors.toMap(GoodsEntity::getId, o -> o, (v1, v2) -> v1));
// 获取标识码
Set<Long> keySet = goodsMap.keySet();
List<CodesEntity> codes = snmsClient.getCodesByMasterIds(new ArrayList<>(keySet));
if (codes != null && codes.size() > 0) {
int size = codes.size();
int temp = size / 500;
int rem = size % 500;
if (rem != 0) {
temp++;
}
ByteArrayOutputStream bo = new ByteArrayOutputStream();
for (int j = 0; j < temp; j++) {
String fileName = System.currentTimeMillis() + ".for";
StringBuffer sb_init = new StringBuffer();
String qiyeCode = "88.331.111"; // 企业编码
sb_init.append("#!A1#DC\n");
sb_init.append("#IMSR50.00/50.00\n");
sb_init.append("#HV60\n");
sb_init.append("#PR6/6/\n");
sb_init.append("#RX0\n");
// 循环生码
int start = 500 * j;
int end = 500 * (j + 1);
if (end > size) {
end = size;
}
for (int i = start; i < end; i++) {
CodesEntity code = codes.get(i);
sb_init.append("#ERN/1//0\n");
sb_init.append("#R0/0\n");
// 货物信息
GoodsEntity goodsEntity = goodsMap.get(code.getMasterId());
String goodsName = goodsEntity.getGoodsName();
String goodsIdNumber = goodsEntity.getIdnumber();
// output.println("#T9.6 #J5.9 #YN901/0/34///物品批次: " + goodsBatch + "#G");
sb_init.append("#T7.6 #J8.7 #YN901/0/32///名称: " + goodsName + "#G\n");
sb_init.append("#T7.6 #J12.5 #YN901/0/32///编码: " + goodsIdNumber + "#G\n");
sb_init.append("#T19.0 #J16.4 #YN901/0/36///" + qiyeCode + "#G\n");
sb_init.append("#T17.0 #J20.6.75 #SQR2/MA/6/// #VW/L/\"" + snmsClient.getApiHost(code.getCode()) + "\"#G\n");
sb_init.append("#T6.5 #J42.1 #YN901/0/25///CANGZHOU FOUR STARS GLASS CO.,LTD#G\n");
sb_init.append("#T6.6 #J45.5 #YN901/0/34///沧 州 四 星 玻 璃 股 份 有 限 公 司\n");
sb_init.append("#Q1#G\n");
}
sb_init.append("#!P1\n");
bo.write(sb_init.toString().getBytes(StandardCharsets.UTF_8));
resMap.put(fileName, bo.toByteArray());
bo.reset();
}
bo.close();
}
return resMap;
}
生成内存流的核心代码逻辑
/**
ByteArrayOutputStream 使用的是 apache.commons 包下的
**/
// 创建map集合 用来存放文件内存流
Map<String, byte[]> resMap = new HashMap();
// 创建内存流
ByteArrayOutputStream bo = new ByteArrayOutputStream();
// 创建StringBuffer 准备进行文本拼接
StringBuffer sb_init = new StringBuffer();
String fileName = ""
for (int i = start; i < end; i++) {
// 文件名名称
fileName = "xxx";
// 进行文本内容拼接 (或者进行自己相关的业务逻辑)
sb_init.append("xxxxx");
// 写入内存流
bo.write(sb_init.toString().getBytes(StandardCharsets.UTF_8));
// 将内存流存入 map集合
resMap.put(fileName, bo.toByteArray());
// 重置内存流数据
bo.reset();
}
// 关闭流
bo.close();
return resMap;