完整版excel上传导入读写批量数据并将反馈结果写入远程exel中
思路:excel的读写借助于poi框架,在写入远程的时候,是不能直接写入的,本博主将传入的文件再次拉下来写到项目临时文件中,然后,在临时文件中写入,然后,以同样的名称路径覆盖掉远程的就可以了,稍微有点绕了,从远端获取文件,需要通过流来写到项目临时文件中,具体见下方代码,代码中有部分业务删减,该代码是在工作中运行通过的。模板是我们自己制定的,所以只适合已有模板。
文件工具类及方法:
1 package com.XXXX.XXXXX.utils; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileNotFoundException; 7 import java.io.FileOutputStream; 8 import java.io.IOException; 9 import java.io.InputStream; 10 import java.net.HttpURLConnection; 11 import java.net.URL; 12 import java.text.DecimalFormat; 13 import java.util.ArrayList; 14 import java.util.List; 15 16 import org.apache.commons.io.FileUtils; 17 import org.apache.poi.hssf.usermodel.HSSFDateUtil; 18 import org.apache.poi.hssf.usermodel.HSSFWorkbook; 19 import org.apache.poi.ss.usermodel.Cell; 20 import org.apache.poi.ss.usermodel.CellType; 21 import org.apache.poi.ss.usermodel.Row; 22 import org.apache.poi.ss.usermodel.Sheet; 23 import org.apache.poi.ss.usermodel.Workbook; 24 import org.apache.poi.ss.usermodel.WorkbookFactory; 25 import org.apache.poi.xssf.usermodel.XSSFWorkbook; 26 import org.slf4j.Logger; 27 import org.slf4j.LoggerFactory; 28 import org.springframework.util.ResourceUtils; 29 30 import com.sun.javafx.scene.control.skin.TreeTableRowSkin; 31 import com.xxx.cloud.common.utils.DateFormatUtil; 32 import com.xxx.cloud.common.utils.StringUtil; 33 34 public class ReadExcelUtil { 35 36 private static final String EXCEL_XLS = ".xls"; 37 private static final String EXCEL_XLSX = ".xlsx"; 38 39 /** 40 *读取excel数据 41 * @throws Exception 42 * 43 */ 44 public static List<List<String>> readExcelInfo(String url) throws Exception{ 45 // 支持excel2003、2007 46 File excelFile = new File(url);//创建excel文件对象 47 InputStream is = new FileInputStream(excelFile);//创建输入流对象 48 checkExcelVaild(excelFile); 49 Workbook workbook = getWorkBook(is, excelFile); 50 // Workbook workbook = WorkbookFactory.create(is);//同时支持2003、2007、2010 51 // 获取Sheet数量 52 int sheetNum = workbook.getNumberOfSheets(); 53 sheetNum = 1;//限制模板只在一个工作簿上操作 54 // 创建二维数组保存所有读取到的行列数据,外层存行数据,内层存单元格数据 55 List<List<String>> dataList = new ArrayList<List<String>>(); 56 // 遍历工作簿中的sheet,第一层循环所有sheet表 57 for(int index = 0;index<sheetNum;index++){ 58 Sheet sheet = workbook.getSheetAt(index); 59 if(sheet==null){ 60 continue; 61 } 62 // 如果当前行没有数据跳出循环,第二层循环单sheet表中所有行 63 for(int rowIndex=0;rowIndex<=sheet.getLastRowNum();rowIndex++){ 64 System.out.println(sheet.getLastRowNum()+"===="); 65 Row row = sheet.getRow(rowIndex); 66 if(row==null){ 67 continue; 68 } 69 // 遍历每一行的每一列,第三层循环行中所有单元格 70 List<String> cellList = new ArrayList<String>(); 71 for(int cellIndex=0;cellIndex<row.getLastCellNum();cellIndex++){ 72 Cell cell = row.getCell(cellIndex); 73 System.out.println(cellIndex); 74 cellList.add(getCellValue(cell)); 75 } 76 dataList.add(cellList); 77 } 78 79 } 80 is.close(); 81 return dataList; 82 } 83 /** 84 *获取单元格的数据,暂时不支持公式 85 * 86 * 87 */ 88 public static String getCellValue(Cell cell){ 89 CellType cellType = cell.getCellTypeEnum(); 90 if(cellType==null){ 91 return null; 92 } 93 String cellValue = ""; 94 if(cell==null || cell.toString().trim().equals("")){ 95 return null; 96 } 97 98 if(cellType==CellType.STRING){ 99 cellValue = cell.getStringCellValue().trim(); 100 return cellValue = StringUtil.isEmpty(cellValue)?"":cellValue; 101 } 102 if(cellType==CellType.NUMERIC){ 103 if (HSSFDateUtil.isCellDateFormatted(cell)) { //判断日期类型 104 cellValue = DateFormatUtil.formatDurationYMD(cell.getDateCellValue().getTime()); 105 } else { //否 106 cellValue = new DecimalFormat("#.######").format(cell.getNumericCellValue()); 107 } 108 return cellValue; 109 } 110 if(cellType==CellType.BOOLEAN){ 111 cellValue = String.valueOf(cell.getBooleanCellValue()); 112 return cellValue; 113 } 114 return null; 115 116 } 117 /** 118 *判断excel的版本,并根据文件流数据获取workbook 119 * @throws IOException 120 * 121 */ 122 public static Workbook getWorkBook(InputStream is,File file) throws Exception{ 123 124 Workbook workbook = null; 125 if(file.getName().endsWith(EXCEL_XLS)){ 126 workbook = new HSSFWorkbook(is); 127 }else if(file.getName().endsWith(EXCEL_XLSX)){ 128 workbook = new XSSFWorkbook(is); 129 } 130 131 return workbook; 132 } 133 /** 134 *校验文件是否为excel 135 * @throws Exception 136 * 137 * 138 */ 139 public static void checkExcelVaild(File file) throws Exception { 140 String message = "该文件是EXCEL文件!"; 141 if(!file.exists()){ 142 message = "文件不存在!"; 143 throw new Exception(message); 144 } 145 if(!file.isFile()||((!file.getName().endsWith(EXCEL_XLS)&&!file.getName().endsWith(EXCEL_XLSX)))){ 146 message = "文件不是Excel"; 147 throw new Exception(message); 148 } 149 } 150 /** 151 *校验上传的excel模板是否正确 152 * 153 * 154 * 155 */ 156 public static boolean checkExcelTemplate(String url){ 157 try { 158 List<List<String>> list = ReadExcelUtil.readExcelInfo(url); 159 for(int i=0;i<list.size();i++){ 160 if(i==0){ 161 if(!list.get(i).get(0).trim().equals("公司ID")||!list.get(i).get(1).trim().equals("产品ID")||!list.get(0).get(2).trim().equals("设备类型") 162 ||!list.get(i).get(3).trim().equals("设备型号")||!list.get(i).get(4).trim().equals("设备名称")){ 163 return false; 164 } 165 } 166 if(i==2){ 167 if(!list.get(i).get(0).trim().equals("设备ID")||!list.get(i).get(1).trim().equals("结果")){ 168 return false; 169 } 170 } 171 } 172 }catch (Exception e) { 173 // TODO Auto-generated catch block 174 e.printStackTrace(); 175 } 176 return true; 177 178 } 179 /** 180 * 将反馈结果写入excel中 181 * */ 182 public static void writeExcelResult(String url,List<Integer> result) throws Exception{ 183 // 支持excel2003、2007 184 File excelFile = new File(url);//创建excel文件对象 185 InputStream is = new FileInputStream(excelFile);//创建输入流对象 186 checkExcelVaild(excelFile); 187 Workbook workbook = getWorkBook(is, excelFile); 188 // Workbook workbook = WorkbookFactory.create(is);//同时支持2003、2007、2010 189 // 获取Sheet数量 190 int sheetNum = workbook.getNumberOfSheets(); 191 sheetNum = 1;//限制模板只在一个工作簿上操作 192 // 遍历工作簿中的sheet,第一层循环所有sheet表 193 for(int index = 0;index<sheetNum;index++){ 194 Sheet sheet = workbook.getSheetAt(index); 195 if(sheet==null){ 196 continue; 197 } 198 // 如果当前行没有数据跳出循环,第二层循环单sheet表中所有行 199 for(int rowIndex=3;rowIndex<=sheet.getLastRowNum();rowIndex++){ 200 Row row = sheet.getRow(rowIndex); 201 row.createCell(1).setCellValue(result.get(rowIndex-3)); 202 } 203 204 } 205 FileOutputStream outputStream = new FileOutputStream(url); 206 workbook.write(outputStream); 207 outputStream.close(); 208 } 209 /** 210 * 根据地址获得客户上传的excel字节流 211 * @param fileUrl 网络连接地址 212 * @return 213 */ 214 public static byte[] getExcelFromAliyun(String fileUrl){ 215 try { 216 URL url = new URL(fileUrl); 217 HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 218 conn.setRequestMethod("GET"); 219 conn.setConnectTimeout(5 * 1000); 220 InputStream inStream = conn.getInputStream();//通过输入流获取excelFile数据 221 byte[] excelFile = readInputStream(inStream);//得到excelFile的二进制数据 222 return excelFile; 223 } catch (Exception e) { 224 e.printStackTrace(); 225 } 226 return null; 227 } 228 /** 229 * 从网上得到的输入流中获取数据转换为二进制数据 230 * @param inStream 输入流 231 * @return 232 * @throws Exception 233 */ 234 public static byte[] readInputStream(InputStream inStream) throws Exception{ 235 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 236 byte[] buffer = new byte[1024]; 237 int len = 0; 238 while( (len=inStream.read(buffer)) != -1 ){ 239 outStream.write(buffer, 0, len); 240 } 241 inStream.close(); 242 return outStream.toByteArray(); 243 } 244 /** 245 * 将文件写入到目标目录中 246 * @param excel 文件数据流 247 * @param fileName 文件保存时的名称 248 */ 249 public static void writeFileToDest(byte[] excelFile, File dest){ 250 try { 251 FileOutputStream out = new FileOutputStream(dest); 252 out.write(excelFile); 253 out.flush(); 254 out.close(); 255 } catch (Exception e) { 256 e.printStackTrace(); 257 } 258 } 259 /*** 260 * 261 * 在项目中创建临时文件 262 * @throws IOException 263 * */ 264 public static File createTempFile(String fileName) throws IOException{ 265 File path = new File(ResourceUtils.getURL("classpath:").getPath()); 266 if(!path.exists()) path = new File(""); 267 File upload = new File(path.getAbsolutePath(),"static/images/upload/"); 268 if(!upload.exists()) upload.mkdirs(); 269 File tempFile = new File(upload+"/"+fileName); 270 if(!tempFile.getParentFile().exists()){ 271 tempFile.getParentFile().mkdirs();//创建父级文件路径 272 tempFile.createNewFile();//创建文件 273 } 274 return tempFile; 275 } 276 }
contrller层代码
1 @PostMapping("/addBatchDevice") 2 public ResponseObj addBatchDevice(@RequestBody JSONObject obj) throws IOException{ 3 logger.info("导入批量设备:"+obj.toJSONString()); 4 ResponseObj response = new ResponseObj(); 5 String id = obj.getString("id"); 6 BatchRecord batchRecord = deviceService.selectBatchRecordById(id); 7 String path = aliConstants.aliyunHostOuter+"/"+batchRecord.getFilePath(); 8 // 将该该文件下载出来保存到项目中 9 byte[] excelFile = ReadExcelUtil.getExcelFromAliyun(path); 10 File tempFile = ReadExcelUtil.createTempFile(batchRecord.getFileName()); 11 ReadExcelUtil.writeFileToDest(excelFile, tempFile); 12 String url = tempFile.getAbsolutePath(); 13 String companyId = null; 14 String productId = null; 15 Integer deviceType = null; 16 String model = null; 17 String deviceName = null; 18 boolean flag = ReadExcelUtil.checkExcelTemplate(url); 19 if(!flag){ 20 response.setData(Defined.STATUS_ERROR); 21 response.setMessage("文件有误,请根据模板上传文件。"); 22 return response; 23 } 24 List<Integer> result = new ArrayList<Integer>();//存放反馈信息 25 try { 26 List<List<String>> list = ReadExcelUtil.readExcelInfo(url); 27 for(int i=0;i<list.size();i++){ 28 if(i==0||i==2){ 29 continue;//跳过模板第1,3行 30 } 31 if(i==1){ 32 companyId = list.get(i).get(0); 33 productId = list.get(i).get(1); 34 deviceType = Integer.valueOf(list.get(i).get(2)); 35 model = list.get(i).get(3); 36 deviceName = list.get(i).get(4); 37 } 38 if(i>2){ 39 // new一个对象按照相应的字段设置进去就可以了,这里省略对象设置值,字段如下: 40 Device device = new Device(); 41 String deviceId = IdGen.uuid(); 42 device.setId(deviceId); 43 //省略部分业务代码 44 DeviceFields deviceFields = new DeviceFields(); 45 deviceFields.setId(IdGen.uuid()); 46 deviceFields.setDeviceId(deviceId); 47 //省略部分业务代码 48 Gateway gateway = new Gateway(); 49 gateway.setId(IdGen.uuid()); 50 //省略部分业务代码 51 if(!deviceService.checkDeviceUidRepeat(uid)){ 52 // 重复,返回sheet行号,并写入sheet表单中 53 result.add(1); 54 continue; 55 } 56 // 关联表一个事务处理 57 boolean flg = deviceService.addDeviceEtc(device, deviceFields, gateway); 58 if(!flg){ 59 result.add(1); 60 }else{ 61 result.add(0); 62 } 63 } 64 } 65 // 将反馈结果写入文件0-成功,1-失败 66 ReadExcelUtil.writeExcelResult(url, result); 67 AliYunFileSetting setting = new AliYunFileSetting(); 68 setting.setAccessKeyId(aliConstants.accessKeyId); 69 setting.setAccessKeySecret(aliConstants.accessKeySecret); 70 setting.setBucketName(aliConstants.bucketName); 71 setting.setEndpoint(aliConstants.endpoint); 72 AliyunFileManager manager = AliyunFileManager.getInstance(setting); 73 InputStream is = new FileInputStream(tempFile); 74 String relativePath =aliConstants.excelFilePath;//相对路径 75 boolean fg = manager.upload(is, relativePath, batchRecord.getFileName());//上传文件与客户上传后生成的文件名相同。 76 if(fg){ 77 logger.info("反馈已经成功写入文件中!"); 78 // 更新批量记录 79 batchRecord.setUpdateTime(DateUtil.getNowTimestamp()); 80 deviceService.updateBatchRecordByPrimaryKey(batchRecord); 81 } 82 } catch (Exception e) { 83 // TODO Auto-generated catch block 84 e.printStackTrace(); 85 } 86 response.setMessage("批量导入设备成功!"); 87 response.setStatus(Defined.STATUS_SUCCESS); 88 return response; 89 } 90 @PostMapping("uploadExcel") 91 public ResponseObj uploadExcel(@RequestParam("excelFile") MultipartFile file,@RequestParam("companyId") String companyId, 92 @RequestParam("productId") String productId,HttpServletRequest request) throws Exception { 93 ResponseObj response = new ResponseObj(); 94 response.setData(Defined.STATUS_SUCCESS); 95 response.setMessage("文件上传成功!"); 96 ResponseObj resp = new ResponseObj(); 97 resp.setData(Defined.STATUS_ERROR); 98 resp.setMessage("不是文件!"); 99 AliYunFileSetting setting = new AliYunFileSetting(); 100 setting.setAccessKeyId(aliConstants.accessKeyId); 101 setting.setAccessKeySecret(aliConstants.accessKeySecret); 102 setting.setBucketName(aliConstants.bucketName); 103 setting.setEndpoint(aliConstants.endpoint); 104 AliyunFileManager manager = AliyunFileManager.getInstance(setting); 105 if(file.isEmpty()){ 106 response.setData(Defined.STATUS_ERROR); 107 response.setMessage("不是文件!"); 108 return response; 109 } 110 String fileName = file.getOriginalFilename(); 111 long fileSize = file.getSize(); 112 File tempFile = ReadExcelUtil.createTempFile(fileName); 113 String relativePath =aliConstants.excelFilePath;//相对路径 114 String dir = aliConstants.aliyunHostOuter+"/"+relativePath;//云端绝对路径 115 File dest = new File(dir); 116 if(!dest.exists()){ //判断文件目录是否存在 117 dest.mkdir();// 118 } 119 try { 120 file.transferTo(tempFile); 121 InputStream is = new FileInputStream(tempFile); 122 String suffix=fileName.substring(fileName.lastIndexOf("."));//获取原始文件后缀.xlxs(含点) 123 String newFileName = IdGen.uuid()+suffix; 124 boolean result = manager.upload(is, relativePath, newFileName);//上传文件,并建立随机文件名。 125 // boolean result = manager.upload(is, dir, fileName);//上传阿里云,固定文件名 126 if(result){ 127 response.setData(dir+"/"+newFileName);//返回新建文件名的绝对路径 128 // 数据库存入相对路径 129 BatchRecord batchRecord = new BatchRecord(); 130 batchRecord.setFileName(newFileName); 131 batchRecord.setFilePath(relativePath+"/"+newFileName); 132 batchRecord.setFileSize(fileSize); 133 batchRecord.setIsDelete((byte) 0); 134 batchRecord.setStatus((byte) 0); 135 batchRecord.setId(IdGen.uuid()); 136 batchRecord.setCreateTime(DateUtil.getNowTimestamp()); 137 // batchRecord.setUpdateTime(DateUtil.getNowTimestamp()); 138 batchRecord.setCompanyId(companyId); 139 batchRecord.setProductId(productId); 140 Integer resultNum = deviceService.addBatchRecord(batchRecord); 141 if(resultNum>0){ 142 tempFile.delete(); 143 return response; 144 } 145 return resp; 146 }else{ 147 resp.setMessage("文件上传失败!"); 148 return resp; 149 } 150 } catch (IllegalStateException e) { 151 // TODO Auto-generated catch block 152 e.printStackTrace(); 153 resp.setMessage("文件上传异常!"); 154 return resp; 155 } catch (IOException e) { 156 // TODO Auto-generated catch block 157 e.printStackTrace(); 158 resp.setMessage("文件上传异常!"); 159 return resp; 160 } 161 }
模板图片:
本博主支持并坚持原创,本博客文章将以原创为主。