欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實現(xiàn)Excel導入導出的步驟詳解

 更新時間:2023年06月04日 16:55:25   作者:梅西庫里RNG  
這篇文章主要為大家詳細介紹了java實現(xiàn)Excel的導入、導出,文中示例代碼介紹的非常詳細,對我們的學習或工作有一定的幫助,感興趣的小伙伴們可以參考一下

一、導入

前言:導入必須用post請求

具體原因在2中敘述

1、Excel導入

總結(jié)一下目標,就是要將excel中的數(shù)據(jù)行、逐一提取,最后得到一個list,這個list的每個元素就是excel的每個數(shù)據(jù)行的實例,之后的操作就是常規(guī)的java邏輯處理了。

可以把這個過程分為三步:

1)接收數(shù)據(jù)
后臺使用 MultipartFile 接收文件數(shù)據(jù)(這里復習一下,springmvc接收參數(shù)的幾種方式)。

前端如果是前后端分離項目我們不用管,
如果是jsp項目可以用文件標簽<input type="file" name="" value=""/>傳參。

2)提取數(shù)據(jù)
提取數(shù)據(jù)的底層是用IO流實現(xiàn)的,我們這里使用封裝好的工具類。excel工具類有很多、很多,我一般是使用適配性最好的,不然一會兒springboot項目、一會兒spring項目等等,還得換不同的工具類;效率什么的不是首要考慮項。
使用工具類后,我們就得到了一個List<List<String>>:Excel的每個數(shù)據(jù)行組成一個List<String>,多個數(shù)據(jù)行就組成了List<List<String>>

這里有一個小坑,說明一下;有時候你導入的數(shù)據(jù),如果是數(shù)字比如32位的卡號等等,用工具類提取出來成了科學計數(shù)法、或者后面加了小數(shù)點;這就說明你用的這個工具類沒有將數(shù)字類型數(shù)據(jù)進行處理,你需要在工具類中找到數(shù)字類型,添加toText()方法。
當然你也可以使用我后面提供的工具類,這個問題已經(jīng)作了處理。

3)將list的元素處理成實例對象,方便后續(xù)處理
在作轉(zhuǎn)換的時候,還可以加一些校驗、限制,比如限制excel導入總行數(shù)不得超過多少、限制某列參數(shù)不能重復等等。

下面提供一下我用的excel導入工具類,springboot或者spring項目都可以用

public class ImportExeclUtil {
    private static int totalRows = 0;// 總行數(shù)
    private static int totalCells = 0;// 總列數(shù)
    private static String errorInfo;// 錯誤信息
    /** 無參構(gòu)造方法 */
    public ImportExeclUtil()
    {
    }
    public static int getTotalRows()
    {
        return totalRows;
    }
    public static int getTotalCells()
    {
        return totalCells;
    }
    public static String getErrorInfo()
    {
        return errorInfo;
    }
    /**
     *
     * 根據(jù)流讀取Excel文件
     *
     *
     * @param inputStream
     * @param isExcel2003
     * @return
     * @see [類、類#方法、類#成員]
     */
    public List<List<String>> read(InputStream inputStream, boolean isExcel2003)
            throws IOException
    {
        List<List<String>> dataLst = null;
        /** 根據(jù)版本選擇創(chuàng)建Workbook的方式 */
        Workbook wb = null;
        if (isExcel2003)
        {
            wb = new HSSFWorkbook(inputStream);
        }
        else
        {
            wb = new XSSFWorkbook(inputStream);
        }
        dataLst = readDate(wb);
        return dataLst;
    }
    /**
     *
     * 讀取數(shù)據(jù)
     *
     * @param wb
     * @return
     * @see [類、類#方法、類#成員]
     */
    private List<List<String>> readDate(Workbook wb)
    {
        List<List<String>> dataLst = new ArrayList<List<String>>();
        /** 得到第一個shell */
        Sheet sheet = wb.getSheetAt(0);
        /** 得到Excel的行數(shù) */
        totalRows = sheet.getPhysicalNumberOfRows();
        /** 得到Excel的列數(shù) */
        if (totalRows >= 1 && sheet.getRow(0) != null)
        {
            totalCells = sheet.getRow(0).getPhysicalNumberOfCells();
        }
        /** 循環(huán)Excel的行 */
        for (int r = 1; r < totalRows; r++)
        {
            Row row = sheet.getRow(r);
            if (row == null)
            {
                continue;
            }
            List<String> rowLst = new ArrayList<String>();
            /** 循環(huán)Excel的列 */
            for (int c = 0; c < getTotalCells(); c++)
            {
                Cell cell = row.getCell(c);
                String cellValue = "";
                if (null != cell)
                {
                    // 以下是判斷數(shù)據(jù)的類型
                    switch (cell.getCellTypeEnum())
                    {
                        case NUMERIC: // 數(shù)字
                            //如果是日期的話
                            if(cell != null && HSSFDateUtil.isCellDateFormatted(cell)){
                                Date d = cell.getDateCellValue();
                                DateFormat formater = new SimpleDateFormat("yyyy/MM/dd");
                                String da = formater.format(d);
                                cellValue = da;
                                break;
                            }
                            cellValue = NumberToTextConverter.toText(cell.getNumericCellValue());
                            break;
                        case STRING: // 字符串
                            cellValue = cell.getStringCellValue();
                            break;
                        case BOOLEAN: // Boolean
                            cellValue = cell.getBooleanCellValue() + "";
                            break;
                        case FORMULA: // 公式
                            cellValue = cell.getCellFormula() + "";
                            break;
                        case BLANK: // 空值
                            cellValue = "";
                            break;
                        case ERROR: // 故障
                            cellValue = "非法字符";
                            break;
                        default:
                            cellValue = "未知類型";
                            break;
                    }
                }
                rowLst.add(cellValue);
            }
            /** 保存第r行的第c列 */
            dataLst.add(rowLst);
        }
        return dataLst;
    }
    /**
     *
     * 根據(jù)Excel表格中的數(shù)據(jù)判斷類型得到值
     *
     * @param cell
     * @return
     * @see [類、類#方法、類#成員]
     */
    /*private static String getCellValue(Cell cell)
    {
        String cellValue = "";
        if (null != cell)
        {
            // 以下是判斷數(shù)據(jù)的類型
            switch (cell.getCellType())
            {
                case HSSFCell.CELL_TYPE_NUMERIC: // 數(shù)字
                    ;: // 數(shù)字
                    if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell))
                    {
                        Date theDate = cell.getDateCellValue();
                        SimpleDateFormat dff = new SimpleDateFormat("yyyy-MM-dd");
                        cellValue = dff.format(theDate);
                    }
                    else
                    {
                        DecimalFormat df = new DecimalFormat("0");
                        cellValue = df.format(cell.getNumericCellValue());
                    }
                    break;
                case HSSFCell.CELL_TYPE_STRING: // 字符串
                    cellValue = cell.getStringCellValue();
                    break;
                case HSSFCell.CELL_TYPE_BOOLEAN: // Boolean
                    cellValue = cell.getBooleanCellValue() + "";
                    break;
                case HSSFCell.CELL_TYPE_FORMULA: // 公式
                    cellValue = cell.getCellFormula() + "";
                    break;
                case HSSFCell.CELL_TYPE_BLANK: // 空值
                    cellValue = "";
                    break;
                case HSSFCell.CELL_TYPE_ERROR: // 故障
                    cellValue = "非法字符";
                    break;
                default:
                    cellValue = "未知類型";
                    break;
            }
        }
        return cellValue;
    }*/
    /**
     *
     * 根據(jù)實體成員變量的類型得到成員變量的值
     *
     * @param realValue
     * @param fields
     * @param f
     * @param cellValue
     * @return
     * @see [類、類#方法、類#成員]
     */
    private static Object getEntityMemberValue(Object realValue, Field[] fields, int f, String cellValue)
    {
        String type = fields[f].getType().getName();
        switch (type)
        {
            case "char":
            case "java.lang.Character":
            case "java.lang.String":
                realValue = cellValue;
                break;
            case "java.util.Date":
                realValue = StringUtils.isBlank(cellValue) ? null : DateUtil.strToDate(cellValue, DateUtil.YYYY_MM_DD);
                break;
            case "java.lang.Integer":
                realValue = StringUtils.isBlank(cellValue) ? null : Integer.valueOf(cellValue);
                break;
            case "int":
            case "float":
            case "double":
            case "java.lang.Double":
            case "java.lang.Float":
            case "java.lang.Long":
            case "java.lang.Short":
            case "java.math.BigDecimal":
                realValue = StringUtils.isBlank(cellValue) ? null : new BigDecimal(cellValue);
                break;
            default:
                break;
        }
        return realValue;
    }
    /**
     *
     * 根據(jù)路徑或文件名選擇Excel版本
     *
     *
     * @param filePathOrName
     * @param in
     * @return
     * @throws IOException
     * @see [類、類#方法、類#成員]
     */
    public static Workbook chooseWorkbook(String filePathOrName, InputStream in)
            throws IOException
    {
        /** 根據(jù)版本選擇創(chuàng)建Workbook的方式 */
        Workbook wb = null;
        boolean isExcel2003 = ExcelVersionUtil.isExcel2003(filePathOrName);
        if (isExcel2003)
        {
            wb = new HSSFWorkbook(in);
        }
        else
        {
            wb = new XSSFWorkbook(in);
        }
        return wb;
    }
    static class ExcelVersionUtil
    {
        /**
         *
         * 是否是2003的excel,返回true是2003
         *
         *
         * @param filePath
         * @return
         * @see [類、類#方法、類#成員]
         */
        public static boolean isExcel2003(String filePath)
        {
            return filePath.matches("^.+\\.(?i)(xls)$");
        }
        /**
         *
         * 是否是2007的excel,返回true是2007
         *
         *
         * @param filePath
         * @return
         * @see [類、類#方法、類#成員]
         */
        public static boolean isExcel2007(String filePath)
        {
            return filePath.matches("^.+\\.(?i)(xlsx)$");
        }
    }
    public static class DateUtil
    {
        // ======================日期格式化常量=====================//
        public static final String YYYY_MM_DDHHMMSS = "yyyy-MM-dd HH:mm:ss";
        public static final String YYYY_MM_DD = "yyyy-MM-dd";
        public static final String YYYY_MM = "yyyy-MM";
        public static final String YYYY = "yyyy";
        public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
        public static final String YYYYMMDD = "yyyyMMdd";
        public static final String YYYYMM = "yyyyMM";
        public static final String YYYYMMDDHHMMSS_1 = "yyyy/MM/dd HH:mm:ss";
        public static final String YYYY_MM_DD_1 = "yyyy/MM/dd";
        public static final String YYYY_MM_1 = "yyyy/MM";
        /**
         *
         * 自定義取值,Date類型轉(zhuǎn)為String類型
         *
         * @param date 日期
         * @param pattern 格式化常量
         * @return
         * @see [類、類#方法、類#成員]
         */
        public static String dateToStr(Date date, String pattern)
        {
            SimpleDateFormat format = null;
            if (null == date)
                return null;
            format = new SimpleDateFormat(pattern, Locale.getDefault());
            return format.format(date);
        }
        /**
         * 將字符串轉(zhuǎn)換成Date類型的時間
         * <hr>
         *
         * @param s 日期類型的字符串<br>
         *            datePattern :YYYY_MM_DD<br>
         * @return java.util.Date
         */
        public static Date strToDate(String s, String pattern)
        {
            if (s == null)
            {
                return null;
            }
            Date date = null;
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            try
            {
                date = sdf.parse(s);
            }
            catch (ParseException e)
            {
                e.printStackTrace();
            }
            return date;
        }
    }
}

再提供一個應用實例

@ApiOperation(value = "以導入excel方式,上傳要申請學分的用戶")
    @GetMapping(value = "/uuApplyUserInfo")
    public AjaxResult uuApplyUserInfo(@RequestParam(value = "files",required = false) MultipartFile files) {
        try {
            //工具類
            ImportExeclUtil readExcelUtil = new ImportExeclUtil();
            List<List<String>> read = readExcelUtil.read(files.getInputStream(), true);
            if (CollectionUtils.isNotEmpty(read)){
                List<ApplyCreditUserDto> importList = read.stream().map(e -> {
                    ApplyCreditUserDto importDto = new ApplyCreditUserDto();
                    importDto.setUserName(e.get(0));
                    importDto.setCreditCardNo(e.get(1));
                    importDto.setCreditCardPwd(e.get(2));
                    return importDto;
                }).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(importList)){
                    return AjaxResult.error("不能導入空文件");
                }
                //最多導入1W條
                final int maxInt = 10000;
                if (importList.size() > maxInt){
                    return AjaxResult.error("導入最多修改1W條");
                }
                List<String> orderIds = importList.stream()
                        .map(ApplyCreditUserDto::getUserName)
                        .distinct()
                        .collect(Collectors.toList());
                if (!Objects.equals(orderIds.size(),importList.size())){
                    return AjaxResult.error("導入信息中,有用戶");
                }
                //調(diào)用業(yè)務層
                return applyCreditLogService.uuApplyUserInfo(importList);
            }else{
                return AjaxResult.error("不能導入空文件");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("導入失敗,更新數(shù)據(jù)庫時報錯!報錯信息:" + e.toString());
        }
    }

2、開發(fā)中遇到的問題

1)報錯"Unable to process parts as no multi-part configuration has been provided "

報錯信息截取
“MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided”
a、如果你在網(wǎng)上搜這個問題,搜到的回答大多是讓你在servlet配置中加配置

<multipart-config>
	<max-file-size>20848820</max-file-size>
	<max-request-size>418018841</max-request-size>
	<file-size-threshold>1048576</file-size-threshold>
</multipart-config>

b、但是采用這個答案前,你要看你的項目是否適用上述情況。
SpringMVC處理multipart請求(解析文件請求),有兩種實現(xiàn):CommonsMultipartResolver和StandardServletMultipartResolver。
他們的區(qū)別我這里不作詳細敘述,就講一點:
在servlet中配置,是StandardServletMultipartResolver的配置方式;
而CommonsMultipartResolver的配置方式,是在applicationContext中配置

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8" />
    <property name="maxUploadSize" value="31457280" />
    <property name="maxInMemorySize" value="4194304" />
</bean>

兩種配置的目標基本一致,但是配置位置、對象不一樣,搞混了不僅解決不了問題,還會導致其他問題。
c、我這樣說,是因為上傳文件是基礎配置,對于已經(jīng)運行很久的項目,上傳配置前輩們應該早已配置完好,后來人遇到的問題,大多不是缺少“基礎”配置引起的,望大家謹慎修改。
d、回到的我的問題,項目采用的是CommonsMultipartResolver解析文件流,我給大家看一下我發(fā)現(xiàn)問題的過程:
請求來了先去MultipartFilter過濾器,檢測請求是否為包含文件流的請求,打斷點可以看到

我不知道人家業(yè)務邏輯是咋處理的,只能step over一步一步往下運行,走到下圖這里我大概明白了一點

這里是判斷這個請求是否為“multipart”類型請求,也就是判斷是否需要專門解析文件流的部件出手,結(jié)果我這兒走到了else里邊!
說明檢測結(jié)論是,我的請求不是multipart請求,或者不符合人家的規(guī)范;
于是我重新請求,進入它的判斷方法multipartResolver.isMultipart(processedRequest);
里邊是

@Override
	public boolean isMultipart(HttpServletRequest request) {
		return (request != null && ServletFileUpload.isMultipartContent(request));
	}

又一層判斷,接著往里邊方法看ServletFileUpload.isMultipartContent(request))

看到這里,我才看明白,人家要求請求必須是Post請求,我尼瑪!浪費足足一天時間啊。

之所以沒記住是因為“知其然而不知其所以然”,只有知道它的正在原因,才好記住長時間不忘。
其實原因很簡單,get請求有大小限制,所以上傳功能需要用post請求。
get請求有大小限制這種說法其實也不準確,其實http協(xié)議對get請求沒有大小、長度限制,限制產(chǎn)生在瀏覽器和服務器。
服務器一般限制get請求大小在8kb以內(nèi);
而瀏覽器又隨型號不通、限制也各不相同,MSIE和Safari的長度限制是2kb,Opear是4kb,F(xiàn)irefox是8KB…
這也解釋了,為什么我搜“get請求大小限制是多少”這個問題,回答都各不一樣,大家都是“知其然而不知其所以然”、得過且過、斷章取義、管中窺豹,我們這種得過且過的態(tài)度,就會使本來簡單的問題變得越來越復雜,因為它本身已經(jīng)疊加了太多其他地方的問題。

根據(jù)瀏覽器限制不同,為了保險你可以取限制最小值2kb,然后說“get請求大小限制為2kb”;
又因為一般瀏覽器限制在4kb,所以又可以說“get請求大小限制為4kb”;
又又因為實際http協(xié)議對get請求沒有限制,所以有人說“get請求沒有大小限制”。。。

嗟乎!希望我們程序員,對自己寫的技術類文檔,都適當嚴謹一些,這樣才能營造出更好的社區(qū)環(huán)境。

二、導出

前言

導出excel可以分為兩類:

一類是導出excel,里邊有我們查詢的數(shù)據(jù),多用于導出數(shù)據(jù);
另一類是導出excel文件,多用于導出模板、而且對模板格式有要求,這時我們提前建一個模板文件,然后存到服務器,導出的時候直接把這個文件傳給用戶,這種方法其實已經(jīng)無所謂是什么格式的文件了。

為了方便描述,我們把第一類叫導出excel,第二類叫下載excel模板

1、導出excel

實際運用示例:

    @PostMapping("/export")
    @ResponseBody
    public void export(HttpServletResponse response, @RequestBody JcWecomTag param){
        // 設置response的上下文類型
        response.setContentType("octets/stream");
        String excelName = "標簽數(shù)據(jù)";
        try {
            // 創(chuàng)建一個文件對象
            HSSFWorkbook workbook = new HSSFWorkbook();
            // 用文件對象創(chuàng)建一個sheet頁,并給sheet頁命名
            HSSFSheet sheet0 = workbook.createSheet("標簽");
            // 用sheet對象創(chuàng)建一個行對象
            HSSFRow rowm0 = sheet0.createRow(0);
            // 用行對象創(chuàng)建單元格,給單元格賦值
            HSSFCell cellColName0 = rowm0.createCell(0);
            cellColName0.setCellType(CellType.STRING);
            HSSFRichTextString text0 = new HSSFRichTextString("標簽ID");
            cellColName0.setCellValue(text0);
            HSSFCell cellColName1 = rowm0.createCell(1);
            cellColName1.setCellType(CellType.STRING);
            HSSFRichTextString text1 = new HSSFRichTextString("標簽名稱");
            cellColName1.setCellValue(text1);
            HSSFCell cellColName2 = rowm0.createCell(2);
            cellColName2.setCellType(CellType.STRING);
            HSSFRichTextString text2 = new HSSFRichTextString("標簽分組ID");
            cellColName2.setCellValue(text2);
            HSSFCell cellColName3 = rowm0.createCell(3);
            cellColName3.setCellType(CellType.STRING);
            HSSFRichTextString text3 = new HSSFRichTextString("標簽分組名稱");
            cellColName3.setCellValue(text3);
            HSSFCell cellColName4 = rowm0.createCell(4);
            cellColName4.setCellType(CellType.STRING);
            HSSFRichTextString text4 = new HSSFRichTextString("bdp字典編碼");
            cellColName4.setCellValue(text4);
            HSSFCell cellColName5 = rowm0.createCell(5);
            cellColName5.setCellType(CellType.STRING);
            HSSFRichTextString text5 = new HSSFRichTextString("bdp字典名稱");
            cellColName5.setCellValue(text5);
            // 查數(shù)據(jù)
            List<JcWecomTag> tagList = wecomTagService.mulitQueryTag(param);
            // 將查詢到的數(shù)據(jù)設置到sheet對應的單元格中
            for (int i = 0; i < tagList.size(); i++) {
                JcWecomTag tag = tagList.get(i);
                HSSFRow row = sheet0.createRow(i + 1);// 創(chuàng)建所需的行數(shù)
                HSSFCell cell = null;
                cell = row.createCell(0, CellType.STRING);
                cell.setCellValue(tag.getTagId());
                cell = row.createCell(1, CellType.STRING);
                cell.setCellValue(tag.getTagName());
                cell = row.createCell(2, CellType.STRING);
                cell.setCellValue(tag.getTagGroupId());
                cell = row.createCell(3, CellType.STRING);
                cell.setCellValue(tag.getTagGroupName());
                cell = row.createCell(4, CellType.STRING);
                cell.setCellValue(tag.getDicCode());
            }
            // 轉(zhuǎn)碼防止亂碼
            response.addHeader("Content-Disposition", "attachment;filename="
                    + new String(excelName.getBytes("UTF-8"), "ISO8859-1")
                    + ".xls");
            OutputStream out = response.getOutputStream();
            ExcelUtil.writeBySheetAndRow(workbook, out);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

1)示例是SpringMVC項目,Controller接口必須加@ResponseBody注解,否則返回值會被SpringMVC視為視圖、會報404錯誤;

2)最后用到了一個excel工具ExcelUtil.writeBySheetAndRow(workbook, out);

這個工具去網(wǎng)上搜一下就行(估計你們項目本身就有)。導出功能的核心是:配置好Excel工作簿對象HSSFWorkbook ,對象包含標題行、數(shù)據(jù)行、單元格數(shù)據(jù);以及配置好輸出流OutputStream;這兩個的配置,如上面示例所示配置即可。
這兩個對象都弄好之后,剩下的、一般Excel工具類都能處理,不用擔心。

2、使用自定義注解-導出excel

1)引入依賴

這里用的是poi導出excel,版本如下

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi</artifactId>
          <version>4.1.1</version>
      </dependency>

2)自定義注解

為了方便,提高復用效率,這里自定義2個注解;
第一個:@EnableExcel,用來開啟Excel表格的導出,用在裝導出數(shù)據(jù)的實體類上;

/**
 *  標記類是否開啟Excel
 * @Author: Sunlong
 * @date: 2020/5/10 20:29
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableExcel {
}

第二個:@ExcelRow,用在裝導出數(shù)據(jù)的實體類的屬性上,用來映射字段與excel的對應關系;

/**
 *  excel 表格 列名注解
 *
 * @author sunlong
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelRow {
    /**
     *  Excel 對應列名
     * @return
     */
    String name();
    /**
     *  excel 列名備注
     * @return
     */
    String note() default "";
}

3)代碼邏輯—提取導出工具類

a、通過反射獲取自定義注解EnableExcel 判斷是否開啟Excel導出

b、通過反射獲取自定義注解ExcelRow 獲取列對應的屬性

c、把屬性對應的列下標取出來,屬性名做為key,下標做為value放到map中

d、遍歷要導出的數(shù)據(jù)集合,通過屬性描述器PropertyDescriptor獲取對應屬性下標及屬性值并設置到cell單元格中

還是為了方便,已經(jīng)提高復用效率,我們將上述代碼提取成一個工具類,如下:

public class ExportExcelUtils {
    /**
     *  workbook
     * @param titleList
     * @return
     */
    public static HSSFWorkbook getWorkBook(List<String> titleList){
        //第一步,創(chuàng)建一個workbook,對應一個Excel文件
        HSSFWorkbook wb = new HSSFWorkbook();
        // 一個sheet
        HSSFSheet sheet = wb.createSheet("sheet1");
        HSSFRow rowTitle = sheet.createRow(0); // 第一行 標題
        // 設置標題
        for (int i = 0; i < titleList.size(); i++) {
            HSSFCell cell = rowTitle.createCell(i);
            cell.setCellValue(titleList.get(i));
        }
        //合并單元格CellRangeAddress構(gòu)造參數(shù)依次表示起始行,截至行,起始列, 截至列
        /*sheet.addMergedRegion(new CellRangeAddress(0,0,0,4));
        sheet.addMergedRegion(new CellRangeAddress(titleList.size()-1,titleList.size()-1,titleList.size()-1,titleList.size()+1));*/
        return wb;
    }
    public static <T> HSSFWorkbook getWorkBook(List<String> titleList , List<T> dataList) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        if (CollectionUtils.isNotEmpty(dataList)) {
            T t1 = dataList.get(0);
            Class<?> t1Class = t1.getClass();
            EnableExcel enableExcel = t1Class.getAnnotation(EnableExcel.class);
            if (enableExcel == null) {
                throw new IllegalArgumentException("EnableExcel 注解沒有在實體類啟用");
            }
            Field[] fields = t1Class.getDeclaredFields();
            if (fields != null && fields.length > 0) {
                Map<String , Integer> titleMap = new HashMap<>(titleList.size()); // 存放屬性名稱對應的下標
                int fieldExcelSize = 0; // 類中ExcelRow 注解的數(shù)量
                for (Field field : fields) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    ExcelRow excelRow = field.getAnnotation(ExcelRow.class);
                    if (excelRow != null) {
                        String name = excelRow.name();
                        if (StringUtils.isEmpty(name)) {
                            throw new IllegalArgumentException("ExcelRow 注解name屬性不能為空");
                        }
                        int index = titleList.indexOf(name.trim());
                        if (index != -1) {
                            fieldExcelSize++;
                            titleMap.put(fieldName , index);
                        }
                    }
                }
                if (!(titleList.size() == fieldExcelSize)) {
                    throw new IllegalArgumentException("ExcelRow 注解name屬性對應的列數(shù)不對");
                }
                HSSFWorkbook workBook = getWorkBook(titleList);
                HSSFSheet sheet = workBook.getSheetAt(0);
                for (T t : dataList) {
                    int lastRowNum = sheet.getLastRowNum();
                    HSSFRow row = sheet.createRow(lastRowNum + 1);
                    BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
                    PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                    for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                        String fieldName = propertyDescriptor.getName();
                        if (titleMap.containsKey(fieldName)) {
                            Method readMethod = propertyDescriptor.getReadMethod();
                            if (readMethod != null) {
                                Class<?> returnType = readMethod.getReturnType();
                                String simpleName = returnType.getSimpleName();
                                Object invoke = readMethod.invoke(t);
                                String value = "";
                                // 可以根據(jù)不同的類型返回不同的數(shù)據(jù)
                                if ("date".equalsIgnoreCase(simpleName)) {
                                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                    if (invoke != null) {
                                        value = simpleDateFormat.format(invoke);
                                    }
                                }
                                if (invoke != null && "".equals(value)) {
                                    value = invoke.toString();
                                }
                                row.createCell(titleMap.get(fieldName)).setCellValue(value);
                            }
                        }
                    }
                }
                return workBook;
            }
        }
        return null;
    }
}

4)應用實例

創(chuàng)建一個用來裝導出數(shù)據(jù)的類(為了不與項目其他功能沖突,我一般都是新建一個專門做導出的實體類)

@EnableExcel
@Data
public class UserEntity {
    @ExcelRow(name = "name")
    private String username;
    @ExcelRow(name = "pass")
    private String password;
    @ExcelRow(name = "date")
    private Date createDate;
}

模擬導出功能:

public class Test {
    public static void main(String[] args) throws IllegalAccessException, IntrospectionException, InvocationTargetException, IOException {
        List<String> titleList = new ArrayList<>();
        titleList.add("name");
        titleList.add("pass");
        titleList.add("date");
        List<UserEntity> userEntities = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            UserEntity userEntity1 = new UserEntity();
            userEntity1.setUsername("username"+i);
            userEntity1.setPassword("password"+i);
            userEntity1.setCreateDate(new Date());
            userEntities.add(userEntity1);
        }
        HSSFWorkbook workBook = ExportExcelUtils.getWorkBook(titleList, userEntities);
        if (workBook != null) {
            File file = new File("D:\\test_export.xlsx");
            workBook.write(file);
        }
    }
}

3、使用easyPOI–導出excel

1)easyPOI簡介

easyPOI是一個以Apache poi為基礎的工具包、一款開源框架,用于實現(xiàn)excel,word,pdf的導入導出;開發(fā)者為Lemur。

特性

基于注解的導入導出,修改注解就可以修改Excel
支持常用的樣式自定義
基于map可以靈活定義的表頭字段
支持一對多的導入、導出
支持模板的導出,一些常見的標簽,自定義標簽
支持HTML/Excel轉(zhuǎn)換
支持word的導出,支持圖片,Excel

2)導出Excel使用實例

a)添加依賴

<dependency>
            <groupId>cn.easyproject</groupId>
            <artifactId>orai18n</artifactId>
        </dependency>

需要注意的是由于easypoi的依賴內(nèi)部依賴原生的poi,所以,引入了easypoi的依賴之后,需要把原生的poi的依賴刪掉

b)需要引入一個easyPoi導入導出工具類

/**
 * 描述:easyPoi導入導出類
 * @author 梅西庫里RNG
 */
@Slf4j
public class EasyPoiExcelUtil {
    public static String dictColIndex="colIndex";
    public static String dictColValue="colDicts";
    /**
     * 常用普通導出
     * @param list
     * @param sheetName
     * @param pojoClass
     * @return
     */
    public static Workbook exportExcel(List<?> list, String sheetName,Class<?> pojoClass) {
        return exportExcel(list,sheetName,pojoClass,null);
    }
    /**
     * 常用普通導出 帶字典
     * @param list
     * @param sheetName
     * @param pojoClass
     * @param dicts
     * @return
     */
    public static Workbook exportExcel(List<?> list, String sheetName,Class<?> pojoClass,List<Map<String,Object>> dicts) {
        return exportExcel(list,null,sheetName,pojoClass,true,dicts);
    }
    /**
     * 常用普通導入
     * @auther zhangdongsheng
     * @param file
     * @param pojoClass
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file,Class<T> pojoClass) {
        return importExcel(file,0,1,pojoClass);
    }
    /**
     * 得到Workbook對象
     *
     * @param file
     * @return
     * @throws IOException
     */
    private static Workbook getWorkBook(MultipartFile file) throws IOException {
        //這樣寫  excel 能兼容03和07
        InputStream is = file.getInputStream();
        Workbook hssfWorkbook = null;
        try {
            hssfWorkbook = new HSSFWorkbook(is);
        } catch (Exception ex) {
            is = file.getInputStream();
            hssfWorkbook = new XSSFWorkbook(is);
        }
        return hssfWorkbook;
    }
    /**
     * 得到錯誤信息
     *
     * @param sb
     * @param list
     * @param i
     * @param obj
     * @param name 用哪個屬性名去表明不和規(guī)定的數(shù)據(jù)
     * @param msg
     * @throws Exception
     */
    private static void getWrongInfo(StringBuilder sb, List list, int i, Object obj, String name, String msg) {
        Class clazz = obj.getClass();
        Object str = null;
        //得到屬性名數(shù)組
        Field[] fields = clazz.getDeclaredFields();
        try{
            for (Field f : fields) {
                if (f.getName().equals(name)) {
                    //用來得到屬性的get和set方法
                    PropertyDescriptor pd = new PropertyDescriptor(f.getName(), clazz);
                    //得到get方法
                    Method getMethod = pd.getReadMethod();
                    str = getMethod.invoke(obj);
                }
            }
            if (i == 0) {
                sb.append(msg + str + ";");
            } else if (i == (list.size() - 1)) {
                sb.append(str + "</br>");
            } else {
                sb.append(str + ";");
            }
        }catch (Exception e){
            log.error("程序異常:{}",e);
        }
    }
    public static Workbook exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, boolean isCreateHeader,List<Map<String,Object>> dicts) {
        ExportParams exportParams = new ExportParams();
        exportParams.setSheetName(sheetName);
        if (title!=null){
            exportParams.setTitle(sheetName);
        }
        exportParams.setCreateHeadRows(isCreateHeader);
        return defaultExport(list, pojoClass, exportParams,dicts);
    }
    public static Workbook exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass) {
        return defaultExport(list, pojoClass, new ExportParams(title, sheetName,ExcelType.XSSF));
    }
    public static Workbook exportExcel(List<Map<String, Object>> list) {
        return defaultExport(list);
    }
    private static Workbook defaultExport(List<?> list, Class<?> pojoClass, ExportParams exportParams) {
        return ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
    }
    private static Workbook defaultExport(List<?> list, Class<?> pojoClass, ExportParams exportParams,List<Map<String,Object>> dicts) {
        int rowCnt=list!=null&&list.size()>0?list.size():0;
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
        if(dicts!=null&&dicts.size()>0){
            if(workbook!=null&&workbook.getNumberOfSheets()>0){
                Sheet sheet =workbook.getSheetAt(0);
                if(rowCnt>0){
                    int curRow=1;//一般會帶頭部 從第1行開始
                    for (int i = 0; i < rowCnt; i++) {
                        int firstRow=i+curRow;
                        for(Map<String,Object> map:dicts) {
                            int firstCol=(int)map.get(dictColIndex);
                            String[] dict=(String[])map.get(dictColValue);
                            CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(firstRow, firstRow, firstCol, firstCol);
                            DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(dict);
                            HSSFDataValidation dataValidation = new HSSFDataValidation(cellRangeAddressList, dvConstraint);
                            sheet.addValidationData(dataValidation);
                        }
                    }
                }
            }
        }
        return workbook;
    }
    private static Workbook defaultExport(List<Map<String, Object>> list) {
        return ExcelExportUtil.exportExcel(list, ExcelType.XSSF);
    }
    private static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
        if (StringUtils.isBlank(filePath)) {
            return new ArrayList<>();
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        List<T> list = null;
        try {
            list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
        }catch (Exception e) {
            log.error("程序異常",e);
        }
        return list;
    }
    private static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
        if (file == null) {
            return new ArrayList<>();
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        List<T> list = null;
        try {
            list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);
        }  catch (Exception e) {
            log.error("程序異常",e);
        }
        return list;
    }
    /**
     * 根據(jù)接收的Excel文件來導入Excel,并封裝成實體類
     *
     * @param file       上傳的文件
     * @param titleRows  表標題的行數(shù)
     * @param headerRows 表頭行數(shù)
     * @param pojoClass  Excel實體類
     */
    private static <T> ExcelImportResult<T> importExcelVerify(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
        if (ObjectUtil.isNull(file)) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        //是否開啟校驗
        params.setNeedVerify(true);
        List<T> list = null;
        try {
            return ExcelImportUtil.importExcelMore(file.getInputStream(), pojoClass, params);
        } catch (Exception e) {
            log.error(">>> 導入數(shù)據(jù)異常:{}", e.getMessage());
            return null;
        }
    }
}

c)Controller方法

@ApiOperation("列表 導出")
    @PostMapping (value="/search/export")
    public void exportOrgList(HttpServletResponse response, @RequestBody CustomerVo customerVo) throws Exception {
    	//查詢
        List<CustomerAdminVo> list = customerService.searchAdmin(customerVo);
		//調(diào)用工具,生成workbook工作簿對象
        Workbook workbook=HysExcelUtil.exportExcel(list,"行政用戶-列表",CustomerAdminVo.class);
		//配置response對象
        response.setContentType("application/x-excel");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition","attachment; filename=customer_admin_export.xls");
		//生成輸出流對象,并把工作薄寫入到輸出流
        OutputStream oStream = response.getOutputStream();// 輸出流
        workbook.write(oStream);//把工作薄寫入到輸出流
        //關閉流
        try {
            oStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

上面代碼中的

HysExcelUtil.exportExcel(list,"行政用戶-列表",CustomerAdminVo.class);

第三個參數(shù)CustomerAdminVo.class,是用來設定導出excel的列名、列順序、與數(shù)據(jù)的映射關系、列的寬度、日期格式等;最好給每一個導出模板,配一個獨立的vo類,用以個性化配置。

@ApiModel(value = "CustomerAdminVo", description = "用戶")
@Data
public class CustomerAdminVo implements Serializable {
    private static final long serialVersionUID = -3477299713883180124L;
    @Excel(name = "賬戶名稱", orderNum = "0", width = 30)
    @ApiModelProperty(value = "賬戶名稱 不為空在賬號表有記錄",dataType ="String",required = true)
    private String accountName;
    @Excel(name = "真實姓名", orderNum = "1", width = 30)
    @ApiModelProperty(value = "真實姓名")
    private String realName;
    @Excel(name = "手機號碼", orderNum = "2", width = 30)
    @ApiModelProperty(name = "mobilNumber",value = "手機號碼",dataType ="String")
    private String mobilNumber;
	@Excel(name = "添加時間", orderNum = "14", expertFormat =  "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
}

其中,用于設置上述內(nèi)容的@Excel注解,就是easyPOI提供的注解

import cn.afterturn.easypoi.excel.annotation.Excel;

注解的屬性中,
name 列名,orderNum 順序,width 列寬,expertFormat 時間字段導出格式(不為空時,按指定格式格式化時間)

d)導出結(jié)果

4、導出excel模板(其實導出word、pdf都可以,就是導出文件的方法)

1)首先,文件要提前放在對應的位置,如下圖:

2)導出的controller方法示例:

@RequestMapping("/downloadTemp")
public void downloadFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String fileName = "org_import.xls"; // 下載的文件名(前提:下載的文件需要存放在服務器對應的位置上)
        response.setContentType("text/html;charset=UTF-8");
        BufferedInputStream in = null;
        BufferedOutputStream out = null;
        request.setCharacterEncoding("UTF-8");
        String rootpath = request.getSession().getServletContext()
                .getRealPath("/");
        try {
            File f = new File(rootpath + "res/" + fileName);//這里文件名,要和文件放的位置對應
            response.setContentType("application/x-excel");
            response.setCharacterEncoding("UTF-8");
            response.setHeader(
                    "Content-Disposition",
                    "attachment; filename="
                            + new String("機構(gòu)導入.xls".getBytes("gbk"),
                            "iso-8859-1"));
            response.setHeader("Content-Length", String.valueOf(f.length()));
            in = new BufferedInputStream(new FileInputStream(f));
            out = new BufferedOutputStream(response.getOutputStream());
            byte[] data = new byte[1024];
            int len = 0;
            while (-1 != (len = in.read(data, 0, data.length))) {
                out.write(data, 0, len);
            }
        } catch (Exception e) {
            out.write(e.toString().getBytes());
            e.printStackTrace();
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

以上就是Java實現(xiàn)Excel導入導出的步驟詳解的詳細內(nèi)容,更多關于Java實現(xiàn)Excel導入導出的資料請關注腳本之家其它相關文章!

相關文章

  • idea離線使用jrebel的超詳細教程

    idea離線使用jrebel的超詳細教程

    IDEA上原生是不支持熱部署的,一般更新了 Java 文件后要手動重啟 Tomcat 服務器,才能生效,下面通過本文給大家分享idea離線使用jrebel的超詳細教程(親測有效),感興趣的朋友一起看看吧
    2020-12-12
  • Java使用connectTo方法提高代碼可續(xù)性詳解

    Java使用connectTo方法提高代碼可續(xù)性詳解

    這篇文章主要介紹了Java使用connectTo方法提高代碼可續(xù)性,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • java獲取每月的最后一天實現(xiàn)方法

    java獲取每月的最后一天實現(xiàn)方法

    下面小編就為大家?guī)硪黄猨ava獲取每月的最后一天實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • SpringBoot全局異常處理與定制404頁面的方法

    SpringBoot全局異常處理與定制404頁面的方法

    這篇文章主要介紹了SpringBoot全局異常處理與定制404頁面的相關資料,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下
    2007-09-09
  • 教你用JAVA寫文本編輯器(三)

    教你用JAVA寫文本編輯器(三)

    這篇文章主要給大家介紹了關于用JAVA寫文本編輯器的相關資料,本文主要實現(xiàn)的是一個點擊選擇文本格式的窗口,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-11-11
  • 淺談MyBatis-plus入門使用

    淺談MyBatis-plus入門使用

    這幾天本人了解到了MyBatis-plus,一個 Mybatis 增強工具包.經(jīng)過一番研究,發(fā)現(xiàn)這玩意真的好用,不用寫任何 xml ,內(nèi)置通用的 Mapper,而且完全是面向?qū)ο缶幊?文檔給的示例代碼,跟之前用過的 sequelize (Node.js 的 ORM)非常像,因此本人也嘗試了一把, 需要的朋友可以參考下
    2021-05-05
  • springboot應用中使用過濾器的過程詳解

    springboot應用中使用過濾器的過程詳解

    過濾器通常用于實現(xiàn)跨切面的功能,例如身份驗證、日志記錄、請求和響應的修改、性能監(jiān)控等,這篇文章主要介紹了springboot應用中使用過濾器,需要的朋友可以參考下
    2023-06-06
  • 基于Springboot一個注解搞定數(shù)據(jù)字典的實踐方案

    基于Springboot一個注解搞定數(shù)據(jù)字典的實踐方案

    這篇文章主要介紹了基于Springboot一個注解搞定數(shù)據(jù)字典問題,大致的方向是自定義注解,在序列化的時候進行數(shù)據(jù)處理,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • java實現(xiàn)把一個List集合拆分成多個的操作

    java實現(xiàn)把一個List集合拆分成多個的操作

    這篇文章主要介紹了java實現(xiàn)把一個List集合拆分成多個的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • java連接mysql數(shù)據(jù)庫的方法

    java連接mysql數(shù)據(jù)庫的方法

    這篇文章主要為大家詳細介紹了java連接mysql數(shù)據(jù)庫的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05

最新評論