文件上传与下载以及导出导出(elmentui+springboot)

近两天用的最多的就是上传下载以及excel的导入和导出,测试人员提的bug不断,走了很多坑,现将其记录下来,以作记录。

首先将应用情况介绍下:


三按钮之 导入数据集:将excel模板中的数据导入到数据库中。(excel具有指定的格式样式等)

三按钮之 导出数据集:将选中的数据库中的某条数据导出到excel。(用的是同一个模板)

三按钮之 导出数据集模板 设计人员希望在导入数据时,首先下载模板,而且可以根据我所选的模板类型,将不变的东西给我填充好,使用者到时候直接填数据就好了,本质也是一个导出。


 <el-button-group>
                    <el-upload
                            :show-file-list="false"
                            :action="upload.importUrl"
                            :on-success="onDataSetUploadSuccess"
                            :on-error="onDataSetUploadError"
                            :file-list="upload.fileList" style="float:left">
                        <el-tooltip class="item" effect="dark" content="导入数据集" placement="bottom">
                            <el-button icon="yx-upload3"></el-button>
                        </el-tooltip>
                    </el-upload>
                    <el-tooltip class="item" effect="dark" content="导出数据集" placement="bottom">
                        <el-button icon="yx-download3" @click="exportFn"></el-button>
                    </el-tooltip>
                    <el-tooltip class="item" effect="dark" content="下载数据集模板" placement="bottom">
                        <el-button icon="yx-download3" @click="SetDownloadFunc"></el-button>
                    </el-tooltip>
                </el-button-group>
   exportFn: function () {
                    var _data = this.$refs.reqMsgTable.selections;
                    if (_data == null || _data.length != 1) {
                        this.$message("请选择一条数据", "提示");
                        return;
                    }
                    for (var i in _data) {
                        var param = '?trdInfId=' + _data[i].trdInfId + "&pkId=" + _data[i].pkId;
                        var url = backend.adminService + "/api/ymit/dataset/export"
                        yufp.util.download(url + param);
                    }
                },


@GetMapping("/export")
	public void download(String trdInfId, String pkId, HttpServletResponse response, HttpServletRequest request) {
		String fileNm = "接口测试数据集模板示例.xls";
		BufferedOutputStream bufferedOutPut = null;
		try {
			String fileName = fileNm.substring(fileNm.lastIndexOf("/") + 1);
			response.setContentType(FileTypeUtil.getMimeType(fileName) + ";charset=UTF-8");
			response.setHeader("Content-Disposition",
					"attachment; filename=" + FileTypeUtil.getEncodeFileName(request, fileName));
			// Workbook workbook = ymitDatasetService.export(trdInfId, pkId);
			Workbook workbook = ymitDatasetService.export2(trdInfId, pkId);
			bufferedOutPut = new BufferedOutputStream(response.getOutputStream());
			bufferedOutPut.flush();
			workbook.write(bufferedOutPut);

		} catch (IOException e) {
			e.printStackTrace();
			throw new YuspException(MessageProvider.getMessage("200001"));
		} finally {
			IOUtils.closeQuietly(bufferedOutPut);
		}
	}

因为开发第一版,并未打算使用文件服务器,所以附件,模板这种东西,目前是在工程中resource下建了一个文件夹来存放。如图所示。

public Workbook export2(String trdInfId, String datasetId) {
		YmitTradeInf ymitTradeInf = new YmitTradeInf();

		ymitTradeInf.setTrdInfId(trdInfId);
		List<YmitTradeInf> list = ymitTradeInfMapper.select(ymitTradeInf);
		if (list.size() > 0) {
			ymitTradeInf = list.get(0);
		}

		DataSetViewVM dataSetViewVM = getDataSetView(datasetId);

		return exportDataset2(dataSetViewVM, ymitTradeInf);

	}

	public Workbook exportDataset2(DataSetViewVM dataSetViewVM, YmitTradeInf ymitTradeInf) {
		InputStream in = null;
		try {
			ClassPathResource classPathResource = new ClassPathResource("tml/接口测试数据集模板示例.xls");
			in = classPathResource.getInputStream();
			Workbook workBook;
			workBook = new HSSFWorkbook(in);
			Sheet sheet = workBook.getSheet(TEMPLATE);
			/* 写入交易信息 */
			trdInfToExcel(sheet.getRow(0), ymitTradeInf);
			/* 写入数据及信息 */
			datasetInfToExcel(sheet.getRow(1), dataSetViewVM);
			List<YmitDataGroup> dataGroups = dataSetViewVM.getDataGroups();
			List<YmitDataItem> dataItems = dataSetViewVM.getDataItems();
			int grpNum = 4;
			if (null != dataGroups) {
				for (YmitDataGroup grp : dataGroups) {
					Row dataTitle = sheet.getRow(2);
					dataTitle.getCell(grpNum).setCellValue(grp.getDatagrpNm());
					int rowIndex = 3;
					for (YmitDataItem item : dataItems) {
						if (item.getDatagrpId().equals(grp.getDatagrpId())) {
							if (grpNum == 4) {
								Row row = sheet.getRow(rowIndex);
								row.getCell(0).setCellValue(rowIndex - 2);
								row.getCell(1).setCellValue(item.getFldNm());
								row.getCell(2).setCellValue(item.getFldDesc());
								row.getCell(3).setCellValue(item.getDefVal());
								row.getCell(4).setCellValue(item.getFldVal());
								rowIndex++;
							} else {
								Row row = sheet.getRow(rowIndex);
								row.getCell(grpNum).setCellValue(item.getFldVal());
								rowIndex++;
							}
						}
					}
					grpNum++;
				}
			}
			return workBook;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			IOUtils.closeQuietly(in);
		}

	}


肯其中业务逻辑的一部分代码没有贴。

导出到此为止

导入

el-upload不多说了,根据参数就可以明白其中的各个属性方法的作用,

 upload: {
                        importUrl: "http://" + config.url + "/api/ymit/dataset/uploadFile?access_token=" + yufp.service.getToken() + "&trdInfId=" + pathId + "&usrCde=" + yufp.session.userName,
                        fileList: [],
                    },

这里因为公司封装的一些方法不太好的缘故,导致唯独上传url给添加上token,所以这里自己拼接的。并添加了想要的参数。

@RequestMapping(value = "/uploadFile")
	public ResultDto<DataSetTemplate> uploadDataSet(MultipartFile file, @RequestParam("trdInfId") String trdInfId,
			@RequestParam("usrCde") String usrCde) {
		DataSetTemplate dataSetTemplate = null;
		try {
			dataSetTemplate = dataSetTemplateService.uploadDataSet(file.getInputStream());
			ymitDatasetService.addDataSetFromTemplate(dataSetTemplate, trdInfId, usrCde);
		} catch (IOException e) {
			ResultDto<DataSetTemplate> result = new ResultDto<>();
			result.setCode(2004);
			result.setMessage("文件格式错误:" + e.getMessage());
		} catch (Throwable e) {
			ResultDto<DataSetTemplate> result = new ResultDto<>();
			result.setCode(2005);
			result.setMessage(e.getMessage());
		}
		return new ResultDto<>(dataSetTemplate);
	}
	public DataSetTemplate uploadDataSet(InputStream is) throws IOException{
		if(is==null) {
			return null;
		}
		DataSetTemplate template=null;
		HSSFWorkbook workBook=null;
		try {
			workBook=new HSSFWorkbook(is);
			HSSFSheet sheet=workBook.getSheet(SHEET_NAME);
			template=parseSheet2DataSetTemplate(sheet);
		}finally {
			if (workBook != null) {
				workBook.close();
			}
		}
		return template;
	}

核心类已经贴完,其实简单来说就是首先spring的MultipartFile 获取流,以此流获得excel 这里就是HSSFWorkbook (03版的)

然后就是读取里面的各行格列数据,然后填充了,很简单。

导入 到此结束

上传附件 下载附件



	@Value("${trdattach.path}")
	private String  attachPath;
// 上传文件会自动绑定到MultipartFile中
	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	public ResultDto<YmitTrdInfAttach> upload(HttpServletRequest request, @RequestParam("file") MultipartFile file,
			@RequestParam("trdInfId") String trdInfId, @RequestParam("usrCde") String usrCde) throws Exception {
		try {
			Map<String, Object> result = new HashMap<String, Object>();
			if (!file.isEmpty()) {
				// 上传文件路径
				String path = attachPath+"/"+SessionUtil.getCurrentProjectCode()+"/"+trdInfId;
				// 上传文件名
				String filename = file.getOriginalFilename();
				if(filename.contains("\\")) {
					filename = filename.substring(filename.lastIndexOf("\\")+1);
				}
				File filepath = new File(path, filename);
				if (!filepath.getParentFile().exists()) {
					filepath.getParentFile().mkdirs();
				}
				long size = file.getSize();
				// 将上传文件保存到一个目标文件当中
				YmitTrdInfAttach ymitTrdInfAttach = new YmitTrdInfAttach();
				ymitTrdInfAttach.setTrdInfId(trdInfId);
				ymitTrdInfAttach.setUpdUsrId(usrCde);
				file.transferTo(new File(filepath.getAbsolutePath()));
				ymitTrdInfAttach.setUpdDt(df.format(new Date()));
				// ymitTrdInfAttach.setUpdUsrId(loginCode);
				ymitTrdInfAttach.setFileNm(filename);

				ymitTrdInfAttach.setFileSize(String.valueOf(size));
				ymitTrdInfAttachService.insertSelective(ymitTrdInfAttach);
				result.put("code", "0");
				result.put("message", "操作成功");
				return new ResultDto(result);
			} else {
				result.put("code", "2");
				result.put("message", "上传文件为空或者已损坏");
				return new ResultDto(result);
			}
		} catch (FileAlreadyExistsException e) {
			logger.error("上传失败",e);
			throw new YuspException(MessageProvider.getMessage(ErrorCodeConst.CODE_200006));
		}

	}
attachPath是yml文件中配置的相对路径 例如

@GetMapping("/download")
	public void download(String fileNm,String trdInfId,HttpServletResponse response, HttpServletRequest request) {
		logger.debug("请求参数:{}", fileNm,trdInfId);
		String path = attachPath+"/"+SessionUtil.getCurrentProjectCode()+"/"+trdInfId;
		File file = new File(path + File.separator + fileNm);
		try {
			if (file.exists() && file.isFile()) {
				byte[] downloadFile = FileUtils.readFileToByteArray(file);
				String fileName = fileNm.substring(fileNm.lastIndexOf("/") + 1);
				response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
				response.setHeader("Content-Disposition",
						"attachment; filename=" + FileTypeUtil.getEncodeFileName(request, fileName));
				response.getOutputStream().write(downloadFile);
				response.getOutputStream().flush();
			} else {
				throw new YuspException(MessageProvider.getMessage(ErrorCodeConst.CODE_200007));
			}
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}

}
FileTypeUtil的作用就是为了适应各种浏览器而进行的操作,本质是一样,例如
/**
     * 根据不同浏览器设置文件名
     *
     * @param request
     * @param filename-文件名称
     */
    public static String getEncodeFileName(HttpServletRequest request, String filename) throws
            UnsupportedEncodingException {
        String userAgent = request.getHeader("User-Agent").toLowerCase();
        if (userAgent.contains("msie") || userAgent.contains("trident/7.0")||userAgent.contains("edge")) {
            return URLEncoder.encode(filename, "UTF-8");
        } else if (userAgent.contains("mozilla") || userAgent.contains("chrome")) {
            return new String(filename.getBytes(), "ISO-8859-1");
        } else {
            return URLEncoder.encode(filename, "UTF-8");
        }
    }

主要是为了解决chrom一般不会出现下载文件文件名乱码的问题,但是微软的IE,edge等都会有问题,所以这里进行了操作,根据您用的是什么浏览器。

代码介绍完毕

踩过的坑:首先说明,这些代码目前测试还算一路顺风。

1 文件路径的问题 用绝对路径不会有问题,但是,不利于维护,而且服务器一般为linux,路径和window或许有差异*(我也不太熟悉)。这里配置的是相对路径,使用相对路径,会有大问题,问题不在于技术本身,而在于spring

MultipartFile的transfer方法,会判断绝对还是相对,如果相对,他会给你添上一些前缀,这一添加,就会导致文件路径不存在,因为我们判断并新建的不是他自动添加的这一个。尝试了各种网上说的方法,首先麻烦,第二,很多时候环境是公司搭建好的,不是我们可以随便修改配置文件的。第三,经本人尝试,真的不能解决问题。

@Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        factory.setMaxFileSize("128KB");
        factory.setMaxRequestSize("128KB");
        return factory.createMultipartConfig();
    }

这种方法亲测,各种问题频出,并不适合新人。

最后问了我的导师,他说这么麻烦吗,他不让用相对路径,那你就转为绝对路径啊

file.transferTo(new File(filepath.getAbsolutePath()));

仅此一个get方法,让网上多少人汗颜。

再次说明,很多例子都是直接用的绝对路径。这里要注意。

第二个坑 excel导出

在本次中因为模板很特殊,而且样式比较奇怪,我不想写代码去构造excel。所以是将模板放在程序中,导出时直接去填充数据就好了,有很多不便之处,但是目前是业务需要。

刚开始为读取到该新增模板路径下的文件煞费苦心,后终得一法,


被删除的方法是读取相对路径的文件,使用过程中,本地调试,没有任何问题,但是到服务器就汇报空指针异常。

后导师看了一眼,说道,服务器上都是jar包了,怎么能读取到呢。要换一个方法。

虽不甚理解,但是换位下边的方法,果然服务器上好用。总结来说改工具就是为了读取jar包吧

第三  spring的 MultipartFile 这个getOriginalFilename获取文件名的方法吧,不同浏览器也不一样,Chrome没问题,但是其他的IE edge openg等浏览器上传文件时,用该法得到的文件名是f://tem//文件夹/a.jpg 带上传电脑路径的,肯定报错了,所以最好还是加一个判断并剔除掉。

到此为止,附件上传,下载,excel导出,导入,功能都已经介绍了一下。以此记录。


  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值