主頁 > 後端開發 > Java實作Excel匯入和匯出,看這一篇就夠了(珍藏版)

Java實作Excel匯入和匯出,看這一篇就夠了(珍藏版)

2021-12-22 08:16:38 後端開發

目錄

前言

1. 功能測驗

1.1 測驗準備

1.2 資料匯入

1.2.1 匯入決議為JSON

1.2.2 匯入決議為物件(基礎)

1.2.3 匯入決議為物件(欄位自動映射)

1.2.4 匯入決議為物件(獲取行號)

1.2.5 匯入決議為物件(獲取原始資料)

1.2.6 匯入決議為物件(獲取錯誤提示)

1.2.7 匯入決議為物件(限制欄位長度)

1.2.8 匯入決議為物件(必填欄位驗證)

1.2.9 匯入決議為物件(資料唯一性驗證)

1.3 資料匯出

1.3.1 動態匯出(基礎)

1.3.2 動態匯出(匯出圖片)

1.3.3 動態匯出(實作下拉串列)

1.3.4 動態匯出(橫向合并)

1.3.5 動態匯出(縱向合并)

1.3.6 匯出模板(基礎)

1.3.7 匯出模板(附示例資料)

1.3.8 按物件匯出(基礎)

1.3.9 按物件匯出(資料映射)

?1.3.10 按物件匯出(調整表頭順序)

2. 環境準備

2.1 Maven 依賴

2.2 類檔案

ExcelUtils

ExcelImport

ExcelExport

ExcelClassField


前言

最近抽了兩天時間,把Java實作表格的相關操作進行了封裝,本次封裝是基于 POI 的二次開發,最終使用只需要呼叫一個工具類中的方法,就能滿足業務中絕大部門的匯入和匯出需求,

1. 功能測驗

1.1 測驗準備

在做測驗前,我們需要將【2. 環境準備】中的四個檔案拷貝在工程里(如:我這里均放在了com.zyq.util.excel 包下),

1.2 資料匯入

1.2.1 匯入決議為JSON

比如,我們有下面一個表格:

Controller 代碼:

@PostMapping("/import")
public JSONArray importUser(@RequestPart("file")MultipartFile file) throws Exception {
    JSONArray array = ExcelUtils.readMultipartFile(file);
    System.out.println("匯入資料為:" + array);
    return array;
}

測驗效果:

1.2.2 匯入決議為物件(基礎)

首先,你需要創建一個與匯入表格對應的Java物體物件,并打上對應的Excel決議的匯入注解,@ExcelImport注解的value則為表頭名稱,

Controller 代碼:

@PostMapping("/import")
public void importUser(@RequestPart("file")MultipartFile file) throws Exception {
    List<User> users = ExcelUtils.readMultipartFile(file, User.class);
    for (User user : users) {
        System.out.println(user.toString());
    }
}

測驗效果:

1.2.3 匯入決議為物件(欄位自動映射)

對于有的列舉資料,通常我們匯入的時候,表格中的資料是值,而在資料保存時,往往用的是鍵,比如:我們用sex=1可以表示為男,sex=2表示為女,那么我們通過配置也可以達到匯入時,資料的自動映射,

那么,我們只需要將Java物體中的物件sex欄位的型別改為對應的數字型別Integer,然后再注解中配置好 kv 屬性(屬性格式為:鍵1-值1;鍵2-值2;鍵3-值3;.....)

Cotroller 代碼略(和 1.2.2 完全一致),

測驗效果:可以看到已經自動映射成功了,

1.2.4 匯入決議為物件(獲取行號)

我們在做頁面資料匯入時,有時候可能需要獲取行號,好追蹤匯入的資料,

那么,我們只需要在對應的物體中加入一個 int 型別的 rowNum 欄位即可,

Cotroller 代碼略(和 1.2.2 完全一致),

測驗效果:

1.2.5 匯入決議為物件(獲取原始資料)

在做頁面資料匯入的時候,如果某行存在錯誤,一般我們會將原始的資料拿出來分析,為什么會造成資料錯誤,那么,我們在物體類中,增加一個 String 型別的 rowData 欄位即可,

Cotroller 代碼略(和 1.2.2 完全一致),

測驗效果:

1.2.6 匯入決議為物件(獲取錯誤提示)

當我們在匯入資料的時候,如果某行資料存在,欄位型別不正確,長度超過最大限制(詳見1.2.7),必填欄位驗證(1.2.8),資料唯一性驗證(1.2.9)等一些錯誤時候,我們可以往物件中添加一個 String 型別的 rowTips 欄位,則可以直接拿到對應的錯誤資訊,

比如,我們將表格中趙子龍的性別改為F(F并不是映射資料),將大喬的性別改為二十八(不能轉換為Integer型別資料),

Cotroller 代碼略(和 1.2.2 完全一致),

測驗效果:可以看到,我們可以通過 rowTips 直接拿到對應的錯誤資料提示,

1.2.7 匯入決議為物件(限制欄位長度)

比如,我們手機通常為11為長度,那么不妨限制電話的最大長度位數為11位,

對應的做法,就是在 @ExcelImport 注解中,設定 maxLength = 11 即可,

比如,我們將諸葛孔明的電話長度設定為超過11位數的一個字串,

Cotroller 代碼略(和 1.2.2 完全一致),

測驗效果:

1.2.8 匯入決議為物件(必填欄位驗證)

我們在做資料匯入的時候,往往還會有一些必填欄位,比如用戶的名稱,電話,

那么,我們只需要在 @ExcelImport 注解屬性中,加上 required = true 即可,

我們將諸葛孔明的電話,以及第4行的姓名去掉,進行測驗,

Cotroller 代碼略(和 1.2.2 完全一致),

測驗效果:

1.2.9 匯入決議為物件(資料唯一性驗證)

(1) 單欄位唯一性驗證

我們在匯入資料的時候,某個欄位是具有唯一性的,比如我們這里假設規定姓名不能重復,那么則可以在對應欄位的 @ExcelImport 注解上加上 unique = true 屬性,

這里我們構建2條姓名一樣的資料進行測驗,

Cotroller 代碼略(和 1.2.2 完全一致),

測驗效果:

(2)多欄位唯一性驗證

如果你匯入的資料存在多欄位唯一性驗證這種情況,只需要將每個對應欄位的 @ExcelImport 注解屬性中,都加上 required = true 即可,

比如:我們將姓名和電話兩個欄位進行聯合唯一性驗證(即不能存在有名稱和電話都一樣的資料,單個欄位屬性重復允許),

首先,我們將剛剛(1)的資料進行匯入,

測驗效果:可以看到,雖然名稱有相同,但電話不相同,所以這里并沒有提示唯一性驗證錯誤,

現在,我們將最后一行的電話也改為和第1行一樣的,于是,現在就存在了違背唯一性的兩條資料,

測驗效果:可以看到,我們的聯合唯一性驗證生效了,

1.3 資料匯出

1.3.1 動態匯出(基礎)

這種方式十分靈活,表中的資料,完全自定義設定,

Controller 代碼:

@GetMapping("/export")
public void export(HttpServletResponse response) {
    // 表頭資料
    List<Object> head = Arrays.asList("姓名","年齡","性別","頭像");
    // 用戶1資料
    List<Object> user1 = new ArrayList<>();
    user1.add("諸葛亮");
    user1.add(60);
    user1.add("男");
    user1.add("https://profile.csdnimg.cn/A/7/3/3_sunnyzyq");
    // 用戶2資料
    List<Object> user2 = new ArrayList<>();
    user2.add("大喬");
    user2.add(28);
    user2.add("女");
    user2.add("https://profile.csdnimg.cn/6/1/9/0_m0_48717371");
    // 將資料匯總
    List<List<Object>> sheetDataList = new ArrayList<>();
    sheetDataList.add(head);
    sheetDataList.add(user1);
    sheetDataList.add(user2);
    // 匯出資料
    ExcelUtils.export(response,"用戶表", sheetDataList);
}

代碼截圖:

由于是 get 請求,我們直接在瀏覽器上輸入請求地址即可觸發下載,

打開下載表格,我們可以看到,表中的資料和我們代碼組裝的順序一致,

1.3.2 動態匯出(匯出圖片)

如果你的匯出中,需要將對應圖片鏈接直接顯示為圖片的話,那么,這里也是可以的,只需要將對應的型別轉為 java.net.URL 型別即可(注意:轉的時候有例外處理,為了方便演示,我這里直接拋出)

測驗效果:

1.3.3 動態匯出(實作下拉串列)

我們在做一些資料匯出的時候,可能要對某一行的下拉資料進行約束限制,

比如,當我們下載一個匯入模版的時候,我們可以將性別,城市對應的列設定為下拉選擇,

測驗效果:

1.3.4 動態匯出(橫向合并)

比如,我們將表頭橫向合并,只需要將合并的單元格設定為 ExcelUtils.COLUMN_MERGE 即可,

測驗效果:可以看到表頭的地址已經被合并了,

1.3.5 動態匯出(縱向合并)

除了橫向合并,我們還可以進行縱向合并,只需要將合并的單元格設定為 ExcelUtils.ROW_MERGE 即可,

測驗效果:

1.3.6 匯出模板(基礎)

我們在做資料匯入的時候,往往首先會提供一個模版供其下載,這樣用戶在匯入的時候才知道如何去填寫資料,匯出模板除了可以用上面的動態匯出,這里還提供了一種更加便捷的寫法,只需要創建一個類,然后再對應欄位上打上 @ExcelExport 注解類即可,

Controller 代碼:

@GetMapping("/export")
public void export(HttpServletResponse response) {
    ExcelUtils.exportTemplate(response, "用戶表", User.class);
}

代碼截圖:

測驗效果:

1.3.7 匯出模板(附示例資料)

我們在做模版下載時候,有時往往會攜帶一條樣本資料,好提示用戶資料格式是什么,那么我們只需要在對應欄位上進行配置即可,

Controller代碼:

測驗效果:

1.3.8 按物件匯出(基礎)

我們還可以通過 List 物件,對資料直接進行匯出,首先,同樣需要在對應類的欄位上,設定匯出名稱,

Controller 代碼:

測驗效果:

1.3.9 按物件匯出(資料映射)

在上面 1.3.8 的匯出中,我們可以看到,性別資料匯出來是1和2,這個不利于用戶體驗,應該需要轉換為對應的中文,我們可以在欄位注解上進行對應的配置,

Controller 代碼略(和1.3.8完全一致)

測驗效果:可以看到1和2顯示為了對應的男和女

1.3.10 按物件匯出(調整表頭順序)

如果你需要對表頭欄位進行排序,有兩種方式:

第一種:按照表格的順序,排列Java類中的欄位;

第二種:在 @ExcelExport 注解中,指定 sort 屬性,其值越少,排名越靠前,

Controller 代碼略(和1.3.8完全一致)

測驗效果:可以看到,此時匯出資料的表頭順序,和我們指定的順序完全一致,

2. 環境準備

2.1 Maven 依賴

本次工具類的封裝主要依賴于阿里巴巴的JSON包,以及表格處理的POI包,所以我們需要匯入這兩個庫的依賴包,另外,我們還需要檔案上傳的相關包,畢竟我們在瀏覽器頁面,做Excel匯入時,是上傳的Excel檔案,

<!-- 檔案上傳 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
	<artifactId>4.5.7</artifactId>
</dependency>
<!-- JSON -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.41</version>
</dependency>
<!-- POI -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.16</version>
</dependency>

2.2 類檔案

ExcelUtils

package com.zyq.util.excel;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import com.zyq.entity.User;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

/**
 * Excel匯入匯出工具類
 *
 * @author sunnyzyq
 * @date 2021/12/17
 */
public class ExcelUtils {

    private static final String XLSX = ".xlsx";
    private static final String XLS = ".xls";
    public static final String ROW_MERGE = "row_merge";
    public static final String COLUMN_MERGE = "column_merge";
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String ROW_NUM = "rowNum";
    private static final String ROW_DATA = "rowData";
    private static final String ROW_TIPS = "rowTips";
    private static final int CELL_OTHER = 0;
    private static final int CELL_ROW_MERGE = 1;
    private static final int CELL_COLUMN_MERGE = 2;
    private static final int IMG_HEIGHT = 30;
    private static final int IMG_WIDTH = 30;
    private static final char LEAN_LINE = '/';
    private static final int BYTES_DEFAULT_LENGTH = 10240;
    private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance();


    public static <T> List<T> readFile(File file, Class<T> clazz) throws Exception {
        JSONArray array = readFile(file);
        return getBeanList(array, clazz);
    }

    public static <T> List<T> readMultipartFile(MultipartFile mFile, Class<T> clazz) throws Exception {
        JSONArray array = readMultipartFile(mFile);
        return getBeanList(array, clazz);
    }

    public static JSONArray readFile(File file) throws Exception {
        return readExcel(null, file);
    }

    public static JSONArray readMultipartFile(MultipartFile mFile) throws Exception {
        return readExcel(mFile, null);
    }

    private static <T> List<T> getBeanList(JSONArray array, Class<T> clazz) throws Exception {
        List<T> list = new ArrayList<>();
        Map<Integer, String> uniqueMap = new HashMap<>(16);
        for (int i = 0; i < array.size(); i++) {
            list.add(getBean(clazz, array.getJSONObject(i), uniqueMap));
        }
        return list;
    }

    /**
     * 獲取每個物件的資料
     */
    private static <T> T getBean(Class<T> c, JSONObject obj, Map<Integer, String> uniqueMap) throws Exception {
        T t = c.newInstance();
        Field[] fields = c.getDeclaredFields();
        List<String> errMsgList = new ArrayList<>();
        boolean hasRowTipsField = false;
        StringBuilder uniqueBuilder = new StringBuilder();
        int rowNum = 0;
        for (Field field : fields) {
            // 行號
            if (field.getName().equals(ROW_NUM)) {
                rowNum = obj.getInteger(ROW_NUM);
                field.setAccessible(true);
                field.set(t, rowNum);
                continue;
            }
            // 是否需要設定例外資訊
            if (field.getName().equals(ROW_TIPS)) {
                hasRowTipsField = true;
                continue;
            }
            // 原始資料
            if (field.getName().equals(ROW_DATA)) {
                field.setAccessible(true);
                field.set(t, obj.toString());
                continue;
            }
            // 設定對應屬性值
            setFieldValue(t,field, obj, uniqueBuilder, errMsgList);
        }
        // 資料唯一性校驗
        if (uniqueBuilder.length() > 0) {
            if (uniqueMap.containsValue(uniqueBuilder.toString())) {
                Set<Integer> rowNumKeys = uniqueMap.keySet();
                for (Integer num : rowNumKeys) {
                    if (uniqueMap.get(num).equals(uniqueBuilder.toString())) {
                        errMsgList.add(String.format("資料唯一性校驗失敗,(%s)與第%s行重復)", uniqueBuilder, num));
                    }
                }
            } else {
                uniqueMap.put(rowNum, uniqueBuilder.toString());
            }
        }
        // 失敗處理
        if (errMsgList.isEmpty() && !hasRowTipsField) {
            return t;
        }
        StringBuilder sb = new StringBuilder();
        int size = errMsgList.size();
        for (int i = 0; i < size; i++) {
            if (i == size - 1) {
                sb.append(errMsgList.get(i));
            } else {
                sb.append(errMsgList.get(i)).append(";");
            }
        }
        // 設定錯誤資訊
        for (Field field : fields) {
            if (field.getName().equals(ROW_TIPS)) {
                field.setAccessible(true);
                field.set(t, sb.toString());
            }
        }
        return t;
    }

    private static <T> void setFieldValue(T t, Field field, JSONObject obj, StringBuilder uniqueBuilder, List<String> errMsgList) {
        // 獲取 ExcelImport 注解屬性
        ExcelImport annotation = field.getAnnotation(ExcelImport.class);
        if (annotation == null) {
            return;
        }
        String cname = annotation.value();
        if (cname.trim().length() == 0) {
            return;
        }
        // 獲取具體值
        String val = null;
        if (obj.containsKey(cname)) {
            val = getString(obj.getString(cname));
        }
        if (val == null) {
            return;
        }
        field.setAccessible(true);
        // 判斷是否必填
        boolean require = annotation.required();
        if (require && val.isEmpty()) {
            errMsgList.add(String.format("[%s]不能為空", cname));
            return;
        }
        // 資料唯一性獲取
        boolean unique = annotation.unique();
        if (unique) {
            if (uniqueBuilder.length() > 0) {
                uniqueBuilder.append("--").append(val);
            } else {
                uniqueBuilder.append(val);
            }
        }
        // 判斷是否超過最大長度
        int maxLength = annotation.maxLength();
        if (maxLength > 0 && val.length() > maxLength) {
            errMsgList.add(String.format("[%s]長度不能超過%s個字符(當前%s個字符)", cname, maxLength, val.length()));
        }
        // 判斷當前屬性是否有映射關系
        LinkedHashMap<String, String> kvMap = getKvMap(annotation.kv());
        if (!kvMap.isEmpty()) {
            boolean isMatch = false;
            for (String key : kvMap.keySet()) {
                if (kvMap.get(key).equals(val)) {
                    val = key;
                    isMatch = true;
                    break;
                }
            }
            if (!isMatch) {
                errMsgList.add(String.format("[%s]的值不正確(當前值為%s)", cname, val));
                return;
            }
        }
        // 其余情況根據型別賦值
        String fieldClassName = field.getType().getSimpleName();
        try {
            if ("String".equalsIgnoreCase(fieldClassName)) {
                field.set(t, val);
            } else if ("boolean".equalsIgnoreCase(fieldClassName)) {
                field.set(t, Boolean.valueOf(val));
            } else if ("int".equalsIgnoreCase(fieldClassName) || "Integer".equals(fieldClassName)) {
                try {
                    field.set(t, Integer.valueOf(val));
                } catch (NumberFormatException e) {
                    errMsgList.add(String.format("[%s]的值格式不正確(當前值為%s)", cname, val));
                }
            } else if ("double".equalsIgnoreCase(fieldClassName)) {
                field.set(t, Double.valueOf(val));
            } else if ("long".equalsIgnoreCase(fieldClassName)) {
                field.set(t, Long.valueOf(val));
            } else if ("BigDecimal".equalsIgnoreCase(fieldClassName)) {
                field.set(t, new BigDecimal(val));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static JSONArray readExcel(MultipartFile mFile, File file) throws IOException {
        boolean fileNotExist = (file == null || !file.exists());
        if (mFile == null && fileNotExist) {
            return new JSONArray();
        }
        // 決議表格資料
        InputStream in;
        String fileName;
        if (mFile != null) {
            // 上傳檔案決議
            in = mFile.getInputStream();
            fileName = getString(mFile.getOriginalFilename()).toLowerCase();
        } else {
            // 本地檔案決議
            in = new FileInputStream(file);
            fileName = file.getName().toLowerCase();
        }
        Workbook book;
        if (fileName.endsWith(XLSX)) {
            book = new XSSFWorkbook(in);
        } else if (fileName.endsWith(XLS)) {
            POIFSFileSystem poifsFileSystem = new POIFSFileSystem(in);
            book = new HSSFWorkbook(poifsFileSystem);
        } else {
            return new JSONArray();
        }
        JSONArray array = read(book);
        book.close();
        in.close();
        return array;
    }

    private static JSONArray read(Workbook book) {
        // 獲取 Excel 檔案第一個 Sheet 頁面
        Sheet sheet = book.getSheetAt(0);
        return readSheet(sheet);
    }

    private static JSONArray readSheet(Sheet sheet) {
        // 首行下標
        int rowStart = sheet.getFirstRowNum();
        // 尾行下標
        int rowEnd = sheet.getLastRowNum();
        // 獲取表頭行
        Row headRow = sheet.getRow(rowStart);
        if (headRow == null) {
            return new JSONArray();
        }
        int cellStart = headRow.getFirstCellNum();
        int cellEnd = headRow.getLastCellNum();
        Map<Integer, String> keyMap = new HashMap<>();
        for (int j = cellStart; j < cellEnd; j++) {
            // 獲取表頭資料
            String val = getCellValue(headRow.getCell(j));
            if (val != null && val.trim().length() != 0) {
                keyMap.put(j, val);
            }
        }
        // 如果表頭沒有資料則不進行決議
        if (keyMap.isEmpty()) {
            return (JSONArray) Collections.emptyList();
        }
        // 獲取每行JSON物件的值
        JSONArray array = new JSONArray();
        // 如果首行與尾行相同,表明只有一行,回傳表頭資料
        if (rowStart == rowEnd) {
            JSONObject obj = new JSONObject();
            // 添加行號
            obj.put(ROW_NUM, 1);
            for (int i : keyMap.keySet()) {
                obj.put(keyMap.get(i), "");
            }
            array.add(obj);
            return array;
        }
        for (int i = rowStart + 1; i <= rowEnd; i++) {
            Row eachRow = sheet.getRow(i);
            JSONObject obj = new JSONObject();
            // 添加行號
            obj.put(ROW_NUM, i + 1);
            StringBuilder sb = new StringBuilder();
            for (int k = cellStart; k < cellEnd; k++) {
                if (eachRow != null) {
                    String val = getCellValue(eachRow.getCell(k));
                    // 所有資料添加到里面,用于判斷該行是否為空
                    sb.append(val);
                    obj.put(keyMap.get(k), val);
                }
            }
            if (sb.length() > 0) {
                array.add(obj);
            }
        }
        return array;
    }

    private static String getCellValue(Cell cell) {
        // 空白或空
        if (cell == null || cell.getCellTypeEnum() == CellType.BLANK) {
            return "";
        }
        // String型別
        if (cell.getCellTypeEnum() == CellType.STRING) {
            String val = cell.getStringCellValue();
            if (val == null || val.trim().length() == 0) {
                return "";
            }
            return val.trim();
        }
        // 數字型別
        if (cell.getCellTypeEnum() == CellType.NUMERIC) {
            // 科學計數法型別
            return NUMBER_FORMAT.format(cell.getNumericCellValue()) + "";
        }
        // 布林值型別
        if (cell.getCellTypeEnum() == CellType.BOOLEAN) {
            return cell.getBooleanCellValue() + "";
        }
        // 錯誤型別
        return cell.getCellFormula();
    }

    public static <T> void exportTemplate(HttpServletResponse response, String fileName, Class<T> clazz) {
        exportTemplate(response, fileName, fileName, clazz, false);
    }

    public static <T> void exportTemplate(HttpServletResponse response, String fileName, String sheetName,
                                          Class<T> clazz) {
        exportTemplate(response, fileName, sheetName, clazz, false);
    }

    public static <T> void exportTemplate(HttpServletResponse response, String fileName, Class<T> clazz,
                                          boolean isContainExample) {
        exportTemplate(response, fileName, fileName, clazz, isContainExample);
    }

    public static <T> void exportTemplate(HttpServletResponse response, String fileName, String sheetName,
                                          Class<T> clazz, boolean isContainExample) {
        // 獲取表頭欄位
        List<ExcelClassField> headFieldList = getExcelClassFieldList(clazz);
        // 獲取表頭資料和示例資料
        List<List<Object>> sheetDataList = new ArrayList<>();
        List<Object> headList = new ArrayList<>();
        List<Object> exampleList = new ArrayList<>();
        Map<Integer, List<String>> selectMap = new LinkedHashMap<>();
        for (int i = 0; i < headFieldList.size(); i++) {
            ExcelClassField each = headFieldList.get(i);
            headList.add(each.getName());
            exampleList.add(each.getExample());
            LinkedHashMap<String, String> kvMap = each.getKvMap();
            if (kvMap != null && kvMap.size() > 0) {
                selectMap.put(i, new ArrayList<>(kvMap.values()));
            }
        }
        sheetDataList.add(headList);
        if (isContainExample) {
            sheetDataList.add(exampleList);
        }
        // 匯出資料
        export(response, fileName, sheetName, sheetDataList, selectMap);
    }

    private static <T> List<ExcelClassField> getExcelClassFieldList(Class<T> clazz) {
        // 決議所有欄位
        Field[] fields = clazz.getDeclaredFields();
        boolean hasExportAnnotation = false;
        Map<Integer, List<ExcelClassField>> map = new LinkedHashMap<>();
        List<Integer> sortList = new ArrayList<>();
        for (Field field : fields) {
            ExcelClassField cf = getExcelClassField(field);
            if (cf.getHasAnnotation() == 1) {
                hasExportAnnotation = true;
            }
            int sort = cf.getSort();
            if (map.containsKey(sort)) {
                map.get(sort).add(cf);
            } else {
                List<ExcelClassField> list = new ArrayList<>();
                list.add(cf);
                sortList.add(sort);
                map.put(sort, list);
            }
        }
        Collections.sort(sortList);
        // 獲取表頭
        List<ExcelClassField> headFieldList = new ArrayList<>();
        if (hasExportAnnotation) {
            for (Integer sort : sortList) {
                for (ExcelClassField cf : map.get(sort)) {
                    if (cf.getHasAnnotation() == 1) {
                        headFieldList.add(cf);
                    }
                }
            }
        } else {
            headFieldList.addAll(map.get(0));
        }
        return headFieldList;
    }

    private static ExcelClassField getExcelClassField(Field field) {
        ExcelClassField cf = new ExcelClassField();
        String fieldName = field.getName();
        cf.setFieldName(fieldName);
        ExcelExport annotation = field.getAnnotation(ExcelExport.class);
        // 無 ExcelExport 注解情況
        if (annotation == null) {
            cf.setHasAnnotation(0);
            cf.setName(fieldName);
            cf.setSort(0);
            return cf;
        }
        // 有 ExcelExport 注解情況
        cf.setHasAnnotation(1);
        cf.setName(annotation.value());
        String example = getString(annotation.example());
        if (!example.isEmpty()) {
            if (isNumeric(example)) {
                cf.setExample(Double.valueOf(example));
            } else {
                cf.setExample(example);
            }
        } else {
            cf.setExample("");
        }
        cf.setSort(annotation.sort());
        // 決議映射
        String kv = getString(annotation.kv());
        cf.setKvMap(getKvMap(kv));
        return cf;
    }

    private static LinkedHashMap<String, String> getKvMap(String kv) {
        LinkedHashMap<String, String> kvMap = new LinkedHashMap<>();
        if (kv.isEmpty()) {
            return kvMap;
        }
        String[] kvs = kv.split(";");
        if (kvs.length == 0) {
            return kvMap;
        }
        for (String each : kvs) {
            String[] eachKv = getString(each).split("-");
            if (eachKv.length != 2) {
                continue;
            }
            String k = eachKv[0];
            String v = eachKv[1];
            if (k.isEmpty() || v.isEmpty()) {
                continue;
            }
            kvMap.put(k, v);
        }
        return kvMap;
    }

    /**
     * 匯出表格到本地
     *
     * @param file      本地檔案物件
     * @param sheetData 匯出資料
     */
    public static void exportFile(File file, List<List<Object>> sheetData) {
        if (file == null) {
            System.out.println("檔案創建失敗");
            return;
        }
        if (sheetData == null) {
            sheetData = new ArrayList<>();
        }
        export(null, file, file.getName(), file.getName(), sheetData, null);
    }

    /**
     * 匯出表格到本地
     *
     * @param <T>      匯出資料類似,和K型別保持一致
     * @param filePath 檔案父路徑(如:D:/doc/excel/)
     * @param fileName 檔案名稱(不帶尾綴,如:學生表)
     * @param list     匯出資料
     * @throws IOException IO例外
     */
    public static <T> File exportFile(String filePath, String fileName, List<T> list) throws IOException {
        File file = getFile(filePath, fileName);
        List<List<Object>> sheetData = getSheetData(list);
        exportFile(file, sheetData);
        return file;
    }

    /**
     * 獲取檔案
     *
     * @param filePath filePath 檔案父路徑(如:D:/doc/excel/)
     * @param fileName 檔案名稱(不帶尾綴,如:用戶表)
     * @return 本地File檔案物件
     */
    private static File getFile(String filePath, String fileName) throws IOException {
        String dirPath = getString(filePath);
        String fileFullPath;
        if (dirPath.isEmpty()) {
            fileFullPath = fileName;
        } else {
            // 判定檔案夾是否存在,如果不存在,則級聯創建
            File dirFile = new File(dirPath);
            if (!dirFile.exists()) {
                dirFile.mkdirs();
            }
            // 獲取檔案夾全名
            if (dirPath.endsWith(String.valueOf(LEAN_LINE))) {
                fileFullPath = dirPath + fileName + XLSX;
            } else {
                fileFullPath = dirPath + LEAN_LINE + fileName + XLSX;
            }
        }
        System.out.println(fileFullPath);
        File file = new File(fileFullPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        return file;
    }

    private static <T> List<List<Object>> getSheetData(List<T> list) {
        // 獲取表頭欄位
        List<ExcelClassField> excelClassFieldList = getExcelClassFieldList(list.get(0).getClass());
        List<String> headFieldList = new ArrayList<>();
        List<Object> headList = new ArrayList<>();
        Map<String, ExcelClassField> headFieldMap = new HashMap<>();
        for (ExcelClassField each : excelClassFieldList) {
            String fieldName = each.getFieldName();
            headFieldList.add(fieldName);
            headFieldMap.put(fieldName, each);
            headList.add(each.getName());
        }
        // 添加表頭名稱
        List<List<Object>> sheetDataList = new ArrayList<>();
        sheetDataList.add(headList);
        // 獲取表資料
        for (T t : list) {
            Map<String, Object> fieldDataMap = getFieldDataMap(t);
            Set<String> fieldDataKeys = fieldDataMap.keySet();
            List<Object> rowList = new ArrayList<>();
            for (String headField : headFieldList) {
                if (!fieldDataKeys.contains(headField)) {
                    continue;
                }
                Object data = fieldDataMap.get(headField);
                if (data == null) {
                    rowList.add("");
                    continue;
                }
                ExcelClassField cf = headFieldMap.get(headField);
                // 判斷是否有映射關系
                LinkedHashMap<String, String> kvMap = cf.getKvMap();
                if (kvMap == null || kvMap.isEmpty()) {
                    rowList.add(data);
                    continue;
                }
                String val = kvMap.get(data.toString());
                if (isNumeric(val)) {
                    rowList.add(Double.valueOf(val));
                } else {
                    rowList.add(val);
                }
            }
            sheetDataList.add(rowList);
        }
        return sheetDataList;
    }

    private static <T> Map<String, Object> getFieldDataMap(T t) {
        Map<String, Object> map = new HashMap<>();
        Field[] fields = t.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                String fieldName = field.getName();
                field.setAccessible(true);
                Object object = field.get(t);
                map.put(fieldName, object);
            }
        } catch (IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return map;
    }

    public static void exportEmpty(HttpServletResponse response, String fileName) {
        List<List<Object>> sheetDataList = new ArrayList<>();
        List<Object> headList = new ArrayList<>();
        headList.add("匯出無資料");
        sheetDataList.add(headList);
        export(response, fileName, sheetDataList);
    }

    public static void export(HttpServletResponse response, String fileName, List<List<Object>> sheetDataList) {
        export(response, fileName, fileName, sheetDataList, null);
    }

    public static void export(HttpServletResponse response, String fileName, String sheetName,
                              List<List<Object>> sheetDataList) {
        export(response, fileName, sheetName, sheetDataList, null);
    }

    public static void export(HttpServletResponse response, String fileName, String sheetName,
                              List<List<Object>> sheetDataList, Map<Integer, List<String>> selectMap) {
        export(response, null, fileName, sheetName, sheetDataList, selectMap);
    }

    public static <T, K> void export(HttpServletResponse response, String fileName, List<T> list, Class<K> template) {
        // list 是否為空
        boolean lisIsEmpty = list == null || list.isEmpty();
        // 如果模板資料為空,且匯入的資料為空,則匯出空檔案
        if (template == null && lisIsEmpty) {
            exportEmpty(response, fileName);
            return;
        }
        // 如果 list 資料,則匯出模板資料
        if (lisIsEmpty) {
            exportTemplate(response, fileName, template);
            return;
        }
        // 匯出資料
        List<List<Object>> sheetDataList = getSheetData(list);
        export(response, fileName, sheetDataList);
    }

    public static void export(HttpServletResponse response, String fileName, List<List<Object>> sheetDataList, Map<Integer, List<String>> selectMap) {
        export(response, fileName, fileName, sheetDataList, selectMap);
    }

    private static void export(HttpServletResponse response, File file, String fileName, String sheetName,
                               List<List<Object>> sheetDataList, Map<Integer, List<String>> selectMap) {
        // 整個 Excel 表格 book 物件
        SXSSFWorkbook book = new SXSSFWorkbook();
        // 每個 Sheet 頁
        Sheet sheet = book.createSheet(sheetName);
        Drawing<?> patriarch = sheet.createDrawingPatriarch();
        // 設定表頭背景色(灰色)
        CellStyle headStyle = book.createCellStyle();
        headStyle.setFillForegroundColor(IndexedColors.GREY_80_PERCENT.index);
        headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headStyle.setAlignment(HorizontalAlignment.CENTER);
        headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
        // 設定表身背景色(默認色)
        CellStyle rowStyle = book.createCellStyle();
        rowStyle.setAlignment(HorizontalAlignment.CENTER);
        rowStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 設定表格列寬度(默認為15個位元組)
        sheet.setDefaultColumnWidth(15);
        // 創建合并演算法陣列
        int rowLength = sheetDataList.size();
        int columnLength = sheetDataList.get(0).size();
        int[][] mergeArray = new int[rowLength][columnLength];
        for (int i = 0; i < sheetDataList.size(); i++) {
            // 每個 Sheet 頁中的行資料
            Row row = sheet.createRow(i);
            List<Object> rowList = sheetDataList.get(i);
            for (int j = 0; j < rowList.size(); j++) {
                // 每個行資料中的單元格資料
                Object o = rowList.get(j);
                int v = 0;
                if (o instanceof URL) {
                    // 如果要匯出圖片的話, 鏈接需要傳遞 URL 物件
                    setCellPicture(book, row, patriarch, i, j, (URL) o);
                } else {
                    Cell cell = row.createCell(j);
                    if (i == 0) {
                        // 第一行為表頭行,采用灰色底背景
                        v = setCellValue(cell, o, headStyle);
                    } else {
                        // 其他行為資料行,默認白底色
                        v = setCellValue(cell, o, rowStyle);
                    }
                }
                mergeArray[i][j] = v;
            }
        }
        // 合并單元格
        mergeCells(sheet, mergeArray);
        // 設定下拉串列
        setSelect(sheet, selectMap);
        // 寫資料
        if (response != null) {
            // 前端匯出
            try {
                write(response, book, fileName);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            // 本地匯出
            FileOutputStream fos;
            try {
                fos = new FileOutputStream(file);
                ByteArrayOutputStream ops = new ByteArrayOutputStream();
                book.write(ops);
                fos.write(ops.toByteArray());
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 合并當前Sheet頁的單元格
     *
     * @param sheet      當前 sheet 頁
     * @param mergeArray 合并單元格演算法
     */
    private static void mergeCells(Sheet sheet, int[][] mergeArray) {
        // 橫向合并
        for (int x = 0; x < mergeArray.length; x++) {
            int[] arr = mergeArray[x];
            boolean merge = false;
            int y1 = 0;
            int y2 = 0;
            for (int y = 0; y < arr.length; y++) {
                int value = arr[y];
                if (value == CELL_COLUMN_MERGE) {
                    if (!merge) {
                        y1 = y;
                    }
                    y2 = y;
                    merge = true;
                } else {
                    merge = false;
                    if (y1 > 0) {
                        sheet.addMergedRegion(new CellRangeAddress(x, x, (y1 - 1), y2));
                    }
                    y1 = 0;
                    y2 = 0;
                }
            }
            if (y1 > 0) {
                sheet.addMergedRegion(new CellRangeAddress(x, x, (y1 - 1), y2));
            }
        }
        // 縱向合并
        int xLen = mergeArray.length;
        int yLen = mergeArray[0].length;
        for (int y = 0; y < yLen; y++) {
            boolean merge = false;
            int x1 = 0;
            int x2 = 0;
            for (int x = 0; x < xLen; x++) {
                int value = mergeArray[x][y];
                if (value == CELL_ROW_MERGE) {
                    if (!merge) {
                        x1 = x;
                    }
                    x2 = x;
                    merge = true;
                } else {
                    merge = false;
                    if (x1 > 0) {
                        sheet.addMergedRegion(new CellRangeAddress((x1 - 1), x2, y, y));
                    }
                    x1 = 0;
                    x2 = 0;
                }
            }
            if (x1 > 0) {
                sheet.addMergedRegion(new CellRangeAddress((x1 - 1), x2, y, y));
            }
        }
    }

    private static void write(HttpServletResponse response, SXSSFWorkbook book, String fileName) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String name = new String(fileName.getBytes("GBK"), "ISO8859_1") + XLSX;
        response.addHeader("Content-Disposition", "attachment;filename=" + name);
        ServletOutputStream out = response.getOutputStream();
        book.write(out);
        out.flush();
        out.close();
    }

    private static int setCellValue(Cell cell, Object o, CellStyle style) {
        // 設定樣式
        cell.setCellStyle(style);
        // 資料為空時
        if (o == null) {
            cell.setCellType(CellType.STRING);
            cell.setCellValue("");
            return CELL_OTHER;
        }
        // 是否為字串
        if (o instanceof String) {
            String s = o.toString();
            if (isNumeric(s)) {
                cell.setCellType(CellType.NUMERIC);
                cell.setCellValue(Double.parseDouble(s));
                return CELL_OTHER;
            } else {
                cell.setCellType(CellType.STRING);
                cell.setCellValue(s);
            }
            if (s.equals(ROW_MERGE)) {
                return CELL_ROW_MERGE;
            } else if (s.equals(COLUMN_MERGE)) {
                return CELL_COLUMN_MERGE;
            } else {
                return CELL_OTHER;
            }
        }
        // 是否為字串
        if (o instanceof Integer || o instanceof Long || o instanceof Double || o instanceof Float) {
            cell.setCellType(CellType.NUMERIC);
            cell.setCellValue(Double.parseDouble(o.toString()));
            return CELL_OTHER;
        }
        // 是否為Boolean
        if (o instanceof Boolean) {
            cell.setCellType(CellType.BOOLEAN);
            cell.setCellValue((Boolean) o);
            return CELL_OTHER;
        }
        // 如果是BigDecimal,則默認3位小數
        if (o instanceof BigDecimal) {
            cell.setCellType(CellType.NUMERIC);
            cell.setCellValue(((BigDecimal) o).setScale(3, RoundingMode.HALF_UP).doubleValue());
            return CELL_OTHER;
        }
        // 如果是Date資料,則顯示格式化資料
        if (o instanceof Date) {
            cell.setCellType(CellType.STRING);
            cell.setCellValue(formatDate((Date) o));
            return CELL_OTHER;
        }
        // 如果是其他,則默認字串型別
        cell.setCellType(CellType.STRING);
        cell.setCellValue(o.toString());
        return CELL_OTHER;
    }

    private static void setCellPicture(SXSSFWorkbook wb, Row sr, Drawing<?> patriarch, int x, int y, URL url) {
        // 設定圖片寬高
        sr.setHeight((short) (IMG_WIDTH * IMG_HEIGHT));
        // (jdk1.7版本try中定義流可自動關閉)
        try (InputStream is = url.openStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            byte[] buff = new byte[BYTES_DEFAULT_LENGTH];
            int rc;
            while ((rc = is.read(buff, 0, BYTES_DEFAULT_LENGTH)) > 0) {
                outputStream.write(buff, 0, rc);
            }
            // 設定圖片位置
            XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, y, x, y + 1, x + 1);
            // 設定這個,圖片會自動填滿單元格的長寬
            anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);
            patriarch.createPicture(anchor, wb.addPicture(outputStream.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String formatDate(Date date) {
        if (date == null) {
            return "";
        }
        SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
        return format.format(date);
    }

    private static void setSelect(Sheet sheet, Map<Integer, List<String>> selectMap) {
        if (selectMap == null || selectMap.isEmpty()) {
            return;
        }
        Set<Entry<Integer, List<String>>> entrySet = selectMap.entrySet();
        for (Entry<Integer, List<String>> entry : entrySet) {
            int y = entry.getKey();
            List<String> list = entry.getValue();
            if (list == null || list.isEmpty()) {
                continue;
            }
            String[] arr = new String[list.size()];
            for (int i = 0; i < list.size(); i++) {
                arr[i] = list.get(i);
            }
            DataValidationHelper helper = sheet.getDataValidationHelper();
            CellRangeAddressList addressList = new CellRangeAddressList(1, 65000, y, y);
            DataValidationConstraint dvc = helper.createExplicitListConstraint(arr);
            DataValidation dv = helper.createValidation(dvc, addressList);
            if (dv instanceof HSSFDataValidation) {
                dv.setSuppressDropDownArrow(false);
            } else {
                dv.setSuppressDropDownArrow(true);
                dv.setShowErrorBox(true);
            }
            sheet.addValidationData(dv);
        }
    }

    private static boolean isNumeric(String str) {
        if ("0.0".equals(str)) {
            return true;
        }
        for (int i = str.length(); --i >= 0; ) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    private static String getString(String s) {
        if (s == null) {
            return "";
        }
        if (s.isEmpty()) {
            return s;
        }
        return s.trim();
    }

}

ExcelImport

package com.zyq.util.excel;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author sunnyzyq
 * @date 2021/12/17
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelImport {

    /** 欄位名稱 */
    String value();

    /** 匯出映射,格式如:0-未知;1-男;2-女 */
    String kv() default "";

    /** 是否為必填欄位(默認為非必填) */
    boolean required() default false;

    /** 最大長度(默認255) */
    int maxLength() default 255;

    /** 匯入唯一性驗證(多個欄位則取聯合驗證) */
    boolean unique() default false;

}

ExcelExport

package com.zyq.util.excel;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author sunnyzyq
 * @date 2021/12/17
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelExport {

    /** 欄位名稱 */
    String value();

    /** 匯出排序先后: 數字越小越靠前(默認按Java類欄位順序匯出) */
    int sort() default 0;

    /** 匯出映射,格式如:0-未知;1-男;2-女 */
    String kv() default "";

    /** 匯出模板示例值(有值的話,直接取該值,不做映射) */
    String example() default "";

}

ExcelClassField

package com.zyq.util.excel;

import java.util.LinkedHashMap;

/**
 * @author sunnyzyq
 * @date 2021/12/17
 */
public class ExcelClassField {

    /** 欄位名稱 */
    private String fieldName;

    /** 表頭名稱 */
    private String name;

    /** 映射關系 */
    private LinkedHashMap<String, String> kvMap;

    /** 示例值 */
    private Object example;

    /** 排序 */
    private int sort;

    /** 是否為注解欄位:0-否,1-是 */
    private int hasAnnotation;

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LinkedHashMap<String, String> getKvMap() {
        return kvMap;
    }

    public void setKvMap(LinkedHashMap<String, String> kvMap) {
        this.kvMap = kvMap;
    }

    public Object getExample() {
        return example;
    }

    public void setExample(Object example) {
        this.example = example;
    }

    public int getSort() {
        return sort;
    }

    public void setSort(int sort) {
        this.sort = sort;
    }

    public int getHasAnnotation() {
        return hasAnnotation;
    }

    public void setHasAnnotation(int hasAnnotation) {
        this.hasAnnotation = hasAnnotation;
    }

}

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/389149.html

標籤:java

上一篇:英特爾 Linux Vulkan 驅動程式的首席開發人員離職;JDK 18 功能集被凍結,進入 Rampdown 第一階段;Ubuntu 禁用 os-prober | 開源日報

下一篇:【python入門到精通】python面向物件三大特點:封裝,繼承,多型

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more