国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

java實(shí)現(xiàn)excel的導(dǎo)入導(dǎo)出(帶參數(shù)校驗(yàn):非空校驗(yàn)、數(shù)據(jù)格式校驗(yàn))

這篇具有很好參考價(jià)值的文章主要介紹了java實(shí)現(xiàn)excel的導(dǎo)入導(dǎo)出(帶參數(shù)校驗(yàn):非空校驗(yàn)、數(shù)據(jù)格式校驗(yàn))。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一、簡(jiǎn)單說(shuō)明

本次封裝引入阿里開(kāi)源框架EasyExcel,EasyExcel是一個(gè)基于Java的簡(jiǎn)單、省內(nèi)存的讀寫(xiě)Excel的開(kāi)源項(xiàng)目。在盡可能節(jié)約內(nèi)存的情況下支持讀寫(xiě)百M(fèi)的Excel。 github地址:GitHub - alibaba/easyexcel: 快速、簡(jiǎn)潔、解決大文件內(nèi)存溢出的java處理Excel工具 。64M內(nèi)存20秒讀取75M(46W行25列)的Excel(3.0.2+版本)

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>

結(jié)構(gòu)圖如下:

java實(shí)現(xiàn)excel的導(dǎo)入導(dǎo)出(帶參數(shù)校驗(yàn):非空校驗(yàn)、數(shù)據(jù)格式校驗(yàn))

1.1結(jié)構(gòu)說(shuō)明:

1.annotation:注解

@ExcelPropertyCheck(自己寫(xiě)的注解用作導(dǎo)入數(shù)據(jù)校驗(yàn))

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ExcelPropertyCheck {
    boolean required() default true; ----是否為空,默認(rèn)不為空。

    boolean checkFormat() default false; ----是否進(jìn)行格式檢驗(yàn),默認(rèn)不進(jìn)行。

    int type() default -1; ----格式檢驗(yàn)類(lèi)型,int 已經(jīng)支持的類(lèi)型有 0->ip、1->端口、2->時(shí)間日期格式

    int length() default -1; ----長(zhǎng)度校驗(yàn), int 字符串的長(zhǎng)度,-1不進(jìn)行校驗(yàn)

}

@ExcelProperty(框架自帶的,用于標(biāo)記excel傳輸類(lèi)和一些通用的導(dǎo)入導(dǎo)出配置)

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelProperty {

    String[] value() default {""}; ----導(dǎo)出時(shí)對(duì)應(yīng)字段的表頭名稱(chēng)

    int index() default -1;----排列順序,最好不要配默認(rèn)按字段順序。

    int order() default Integer.MAX_VALUE; ----同上

    Class<? extends Converter<?>> converter() default AutoConverter.class; ----轉(zhuǎn)換器

    String format() default ""; ----格式劃輸出
}

特別提醒:!??!

@ExcelPropertyCheck該注解作用類(lèi)上時(shí)只支持required,其余屬性無(wú)效。字段上的注解配置會(huì)覆蓋類(lèi)上的配置。 ?

2.constant:常量類(lèi),格式校驗(yàn)的類(lèi)型定義,如電話(huà)號(hào)碼、日期、IP地址等。

/**
 * excel導(dǎo)入字段類(lèi)型常量
 */
public class ExcelPropertyType {
    //時(shí)間日期格式校驗(yàn)
    public static final int DATE_TIME = 0;
}

3.converter:轉(zhuǎn)換器

讀寫(xiě)均可使用,實(shí)現(xiàn)Converter重寫(xiě)方法即可。

如將excel 中的日期轉(zhuǎn)為 LocalDateTime 或者將LocalDateTime 轉(zhuǎn)為excel表中的日期

或者將數(shù)據(jù)庫(kù)的枚舉值0,1,2,3導(dǎo)入到excel文件變成對(duì)應(yīng)的中文漢字

/**
 * String and string converter
 *
 */
public class CustomStringStringConverter implements Converter<String> {
    /**
     * 這里讀的時(shí)候會(huì)調(diào)用
     *
     * @param context
     * @return
     */
    @Override
    public String convertToJavaData(ReadConverterContext<?> context) {
        return "自定義:" + context.getReadCellData().getStringValue();
    }

    /**
     * 這里是寫(xiě)的時(shí)候會(huì)調(diào)用 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
        return new WriteCellData<>(context.getValue());
    }

}

上述代碼中泛型為String 表示此字段經(jīng)過(guò)處理后最后返回的類(lèi)型為String?

4.listener:監(jiān)聽(tīng)器

ReadListener<T>(EasyExcel提供的)

ReadListener<T>讀監(jiān)聽(tīng)器,框架提供。提供讀取excel不同時(shí)期的監(jiān)聽(tīng)方法。
分別為:
onException()-------------->“讀取發(fā)生異常時(shí)監(jiān)聽(tīng)”、
invokeHead()----------------->“讀取表頭信息監(jiān)聽(tīng)”、
invoke()--------------------->“讀取每行數(shù)據(jù)監(jiān)聽(tīng)”、
doAfterAllAnalysed()----------->“所有數(shù)據(jù)讀取完畢監(jiān)聽(tīng)”。

BaseListener<T>我們自己封裝的讀監(jiān)聽(tīng)器,在里面結(jié)合注解配置完成數(shù)據(jù)校驗(yàn)。

BaseListener<T>我們自己封裝的讀監(jiān)聽(tīng)器,在里面結(jié)合注解配置完成數(shù)據(jù)校驗(yàn)。
對(duì)外提供的字段:
    private final List<Map<String, Object>> mapData = new ArrayList<>();
    private final List<T> data = new ArrayList<>();
    private final Map<Integer, String> errorMessageMap = new HashMap<>();
    //非空校驗(yàn)map
    private final Map<String, Boolean> nullAbleFieldMap = new HashMap<>();
    //格式校驗(yàn)map
    private final Map<String, String> checkFormatFieldMap = new HashMap<>();
    //長(zhǎng)度校驗(yàn)map    
    private final Map<String, Integer> checkLengthFieldMap = new HashMap<>();
    //枚舉值校驗(yàn)map
    private final Map<String, String[]> checkEnumFieldMap = new HashMap<>();
字段 描述 備注? ? ? ?
mapData excel讀取完畢的Map數(shù)據(jù) Map<String,Object>
data excel讀取完畢的對(duì)于實(shí)體List List<T>
errorMessageMap 錯(cuò)誤信息map Map<Integer,String>;key為錯(cuò)誤行號(hào),value為描述。
nullAbleFieldMap
非空校驗(yàn)map
Map<String, Boolean>
checkFormatFieldMap
格式校驗(yàn)map:電話(huà)號(hào)碼,ip,日期
Map<String, String>
checkLengthFieldMap
長(zhǎng)度校驗(yàn)map
Map<String, Integer>
checkEnumFieldMap
枚舉值檢驗(yàn)map
Map<String, String[]>

??

結(jié)合注解做參數(shù)校驗(yàn)或者格式校驗(yàn)的實(shí)現(xiàn)思路(反射加泛型)

?步驟一:在表頭讀取的監(jiān)聽(tīng)方法里利用反射判斷檢驗(yàn)注解ExcelPropertyCheck加在了那些ExcelDTO哪些屬性上,并且ExcelPropertyCheck注解的具體屬性是什么將這些存入分門(mén)別類(lèi)的map里面。

步驟二:在讀取每行數(shù)據(jù)的時(shí)候,結(jié)合步驟一的map,判斷數(shù)據(jù)是否需要校驗(yàn),并且從map中取出校驗(yàn)的類(lèi)型是什么,再去完成校驗(yàn),如果校驗(yàn)失敗則將錯(cuò)誤信息放入errorMessageMap,當(dāng)前讀取的excel這一行數(shù)據(jù)全部校驗(yàn)通過(guò)則數(shù)據(jù)放入data 里面

完整的BaseListener<T>代碼如下:

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.holder.ReadSheetHolder;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.*;


public class BaseListener<T> implements ReadListener<T> {

    private final Logger logger = LoggerFactory.getLogger(BaseListener.class);
    private Class<?> headClazz;
    //讀取數(shù)據(jù)map形式
    private final List<Map<String, Object>> mapData = new ArrayList<>();
    //讀取數(shù)據(jù)實(shí)體類(lèi)泛型形式
    private final List<T> data = new ArrayList<>();
    //非空校驗(yàn)map
    private final Map<String, Boolean> nullAbleFieldMap = new HashMap<>();
    //格式校驗(yàn)map
    private final Map<String, String> checkFormatFieldMap = new HashMap<>();
    //長(zhǎng)度校驗(yàn)map    
    private final Map<String, Integer> checkLengthFieldMap = new HashMap<>();
    //枚舉值校驗(yàn)map
    private final Map<String, String[]> checkEnumFieldMap = new HashMap<>();
    //數(shù)據(jù)校驗(yàn)錯(cuò)誤信息map key:錯(cuò)誤的行號(hào)  value:錯(cuò)誤信息描述
    private final Map<Integer, String> errorMessageMap = new HashMap<>();

    public List<Map<String, Object>> getMapData() {
        return mapData;
    }

    public List<T> getData() {
        return data;
    }

    public Map<Integer, String> getErrorMessageMap() {
        return errorMessageMap;
    }

    public BaseListener() {
    }

    /**
     * @param headClazz excel model 類(lèi)對(duì)象
     */
    public BaseListener(Class<?> headClazz) {
        this.headClazz = headClazz;
    }

    /**
     * 讀取發(fā)生異常時(shí)的方法
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        logger.debug("發(fā)生了異常");
    }

    /**
     * 讀取頭信息
     *
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        ExcelPropertyCheck clazzHeadAnno = this.headClazz.getAnnotation(ExcelPropertyCheck.class);
        Field[] declaredFields = headClazz.getDeclaredFields();
        if (clazzHeadAnno != null && clazzHeadAnno.required()) {
            for (Field declaredField : declaredFields) {
                nullAbleFieldMap.put(declaredField.getName(), true);
            }
        }

        for (Field declaredField : declaredFields) {
            ExcelPropertyCheck annotation = declaredField.getAnnotation(ExcelPropertyCheck.class);
            if (annotation != null) {
                if (annotation.checkFormat()) {
                    checkFormatFieldMap.put(declaredField.getName(), annotation.type() + "");
                }
                if (annotation.required()) {
                    nullAbleFieldMap.put(declaredField.getName(), true);
                } else {
                    nullAbleFieldMap.remove(declaredField.getName());
                }
                if (annotation.required() && annotation.length() != -1) {
                    checkLengthFieldMap.put(declaredField.getName(), annotation.length());
                }
                if (annotation.required() && annotation.value().length != 0) {
                    checkEnumFieldMap.put(declaredField.getName(), annotation.value());
                }

            }
        }
    }

    /**
     * 讀取每一行數(shù)據(jù)
     *
     * @param t
     * @param analysisContext
     */
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        int rowIndex = ((ReadSheetHolder) analysisContext.currentReadHolder()).getRowIndex() + 1;
        StringBuilder error = new StringBuilder();
        Field[] declaredFields = t.getClass().getDeclaredFields();
        //必填校驗(yàn)和格式校驗(yàn)
        for (Field declaredField : declaredFields) {
            try {
                declaredField.setAccessible(true);
                if (nullAbleFieldMap.get(declaredField.getName()) != null) {
                    if (nullAbleFieldMap.get(declaredField.getName())) {
                        Object o = declaredField.get(t);
                        if (!Objects.nonNull(o)) {
                            error.append(declaredField.getName()).append("為空;");
                        } else {
                            //字段不為空進(jìn)行長(zhǎng)度校驗(yàn)
                            if (checkLengthFieldMap.get(declaredField.getName()) != null) {
                                if (String.valueOf(o).length() > checkLengthFieldMap.get(declaredField.getName())) {
                                    error.append(declaredField.getName()).append("長(zhǎng)度錯(cuò)誤;");
                                }
                            }
                            if (checkEnumFieldMap.get(declaredField.getName()) != null) {
                                if (Integer.parseInt(String.valueOf(o)) == -1) {
                                    error.append(declaredField.getName()).append("枚舉值錯(cuò)誤;");
                                }
                            }
                        }
                    }
                }
                //是否需要進(jìn)行格式校驗(yàn)
                if (checkFormatFieldMap.get(declaredField.getName()) != null) {
                    String res = check(String.valueOf(declaredField.get(t)), Integer.valueOf(checkFormatFieldMap.get(declaredField.getName())));
                    if (StringUtils.hasText(res)) {
                        error.append(res);
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (StringUtils.hasText(error.toString())) {
            errorMessageMap.put(rowIndex, error.toString());
        }
        mapData.add(BeanUtils.beanToMap(t));
        data.add(t);
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        logger.info("excel解析完畢,共解析{}條數(shù)據(jù),錯(cuò)誤數(shù)據(jù){}條,錯(cuò)誤詳情{}", data.size(), errorMessageMap.size(), errorMessageMap);
    }

    private String check(String str, Integer checkType) {
        switch (checkType) {
            case 0:
                return DateUtil.isDateTimeFormat(str) ? "" : "日期格式錯(cuò)誤;";
            default:
                return "";
        }
    }
}

如果導(dǎo)入時(shí)除了簡(jiǎn)單校驗(yàn)還需要特色校驗(yàn)如數(shù)據(jù)庫(kù)去重,業(yè)務(wù)判斷等,請(qǐng)另外寫(xiě)一個(gè)xxxReadListener 來(lái)繼承BaseListener<T> 泛型為對(duì)應(yīng)excel傳輸對(duì)象。重寫(xiě)監(jiān)聽(tīng)方法時(shí)請(qǐng)務(wù)必super()一下,否則基本校驗(yàn)就沒(méi)了!

5.model:excel傳輸對(duì)象

注解配置

為了不污染實(shí)體類(lèi)和字段冗余請(qǐng)?jiān)谶@里定義excel傳輸對(duì)象加注解。

@Data
@EqualsAndHashCode
@ExcelPropertyCheck
public class DemoExcelDto {
    @ExcelProperty("id")
    @ExcelPropertyCheck(required = false)
    private Long id;         //記錄標(biāo)識(shí)
    @ExcelProperty("導(dǎo)入時(shí)間")
    @ExcelPropertyCheck(required = true, checkFormat = true, type = ExcelPropertyType.DATE_TIME)
    private LocalDateTime importTime;
}

二、使用說(shuō)明

1.讀Excel

EasyExcelUtils.importExcel(file, IpDepartsExcelDto.class, listener)

@RequestMapping("importExcel")
@ResponseBody
public Result importIps(MultipartFile file) {
    //XXXReadListener listener = new XXXReadListener (XXXExcelDto.class);
    //沒(méi)有復(fù)雜的校驗(yàn)就用BaseListener
    BaseListener<DemoExcelDto> listener = new BaseListener<>();
    //獲取校驗(yàn)錯(cuò)誤信息
    Map errorMap = EasyExcelUtils.importExcel(file, IpDepartsExcelDto.class, listener);
    //獲取讀取完畢的數(shù)據(jù),即使發(fā)生校驗(yàn)錯(cuò)誤也會(huì)繼續(xù)讀取
    List<IpDeparts> ipDeparts = EasyExcelUtils.dto2model(listener.getData() ,new IpDeparts());
    if (errorMap.size() != 0) {
        //errorMap校驗(yàn)數(shù)據(jù)不為空就是excel數(shù)據(jù)校驗(yàn)有不通過(guò)的數(shù)據(jù)。
        return Result.failure(CommonResultStatus.IMPORT_FAIL, errorMap.toString());
    } else {
        //mapper保存數(shù)據(jù)庫(kù)。
        mapper.saveAll(ipDeparts);
        return Result.success(CommonResultStatus.OK);
    }
}

2.寫(xiě)Excel

EasyExcelUtils.downloadExcel(response, fileName, ipDepartsExcelDtoList, IpDepartsExcelDto.class)

    @GetMapping("download")
    public void download(HttpServletResponse response) throws UnsupportedEncodingException {
        String fileName = URLEncoder.encode("test", "utf-8");
        List<DemoExcelDto> demoExcelDtos = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            DemoExcelDto demoExcelDto = new DemoExcelDto(i, "2020-08-01");
            demoExcelDtos.add(demoExcelDto);
        }
        try {
            EasyExcelUtils.downloadExcel(response, fileName, demoExcelDtos, DemoExcelDto.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

三、EasyExcelUtils(簡(jiǎn)單的導(dǎo)入導(dǎo)出工具類(lèi))

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.BeanUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class EasyExcelUtils {
    /**
     * 導(dǎo)出excel
     *
     * @param response
     * @param fileName 文件名
     * @param data     List<數(shù)據(jù)集合>
     * @throws IOException
     */
    public static <T> void downloadExcel(HttpServletResponse response, String fileName, List<T> data, Class<?> clazz) throws IOException {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 這里需要設(shè)置不關(guān)閉流
            EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).autoCloseStream(Boolean.FALSE).sheet("sheet1")
                    .doWrite(data);
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = MapUtils.newHashMap();
            map.put("status", "failure");
            map.put("message", "下載文件失敗" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    /**
     * 讀取excel 文件
     *
     * @param file     文件
     * @param cl       類(lèi)對(duì)象
     * @param listener 參數(shù)校驗(yàn)監(jiān)聽(tīng)器
     */
    public static <T> Map importExcel(MultipartFile file, Class<T> cl, BaseListener<T> listener) {
        try {
            EasyExcel.read(file.getInputStream(), cl, listener).sheet().doRead();
            return listener.getErrorMessageMap();
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * excleDto 轉(zhuǎn)為對(duì)應(yīng)model實(shí)體類(lèi)
     *
     * @param data
     * @param t
     * @param <T>
     * @return
     */
    public static <T> List<T> dto2model(Collection<?> data, T t) {
        return data.stream().map(e -> {
            T t1 = null;
            try {
                t1 = (T) t.getClass().newInstance();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            BeanUtils.copyProperties(e, t1);
            return t1;
        }).collect(Collectors.toList());
    }
}

四、總結(jié):

經(jīng)過(guò)簡(jiǎn)單的封裝完成了一個(gè)帶參數(shù)校驗(yàn)的簡(jiǎn)單使用案例,直接引入項(xiàng)目后在controller直接調(diào)用EasyExcelUtils的 "importExcel"方法和"downloadExcel"就能實(shí)現(xiàn)excel的導(dǎo)入導(dǎo)出,是不是很方便呢。

有問(wèn)題留言大家一起交流學(xué)習(xí)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-430294.html

到了這里,關(guān)于java實(shí)現(xiàn)excel的導(dǎo)入導(dǎo)出(帶參數(shù)校驗(yàn):非空校驗(yàn)、數(shù)據(jù)格式校驗(yàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包