1. 程式人生 > 其它 >easypoi(基於poi)匯入資料以及模板匯出資料遇到的問題以及部分解決方式

easypoi(基於poi)匯入資料以及模板匯出資料遇到的問題以及部分解決方式

easypoi匯入圖片路徑自定義 easypoi刪除workbook中的圖片 easypoi匯入後圖片大小發生變化

專案在直接使用poi進行excel資料的操作過於繁瑣,於是使用了封裝過的easypoi但是在使用的過程中遇到了一些不滿足需求以及存在的小問題,於是寫部落格記錄一下。

問題:

1、在對驗證失敗的資料進行匯出時,想到將資料的集合在此匯出到一個新的workbook中,然後在匯出到出版中,但是這個過程中測試出,圖片始終不能匯出到模板檔案中。

2、在匯出到模板過程中對於excel樣式的操作過於繁瑣,於是放棄建立的的workbook,採用再原來的workbook中進行操作。

3、在原來的workbook中進行操作時又遇到了一個新問題,在easypoi通過工具類過去excel中的圖片後,excel中的圖片會被初始化到剛插入到excel中時的大小。

4、在失敗的workbook中校驗成功的資料的圖片是沒有被刪除的

解決方案:

1、圖片大小發生變化

問題原因:

通過debug除錯後發現圖片大小發生變化話是因為PoiPublicUtil.getSheetPictrues07中在獲取圖片所在的行號-列號時,呼叫了XSSFClientAnchor anchor = pic.getPreferredSize();方法來過去XSSFClientAnchor 物件,getPreferredSize()此方法中會計算圖片的首選比例(Calculate the preferred size for this picture.),所以不通過 pic.getPreferredSize()

此方法獲取XSSFClientAnchor 此物件就可以保證影象不會被縮放。

解決方案:

通過檢視poi的API發現XSSFClientAnchor 物件可以通過XSSFPicture.getClientAnchor來過去,所以重寫後的方法如下。

    /**
     * 獲取Excel2007圖片
     *
     * @param sheet    當前sheet物件
     * @param workbook 工作簿物件
     * @return Map key:圖片單元格索引(1_1)String,value:圖片流PictureData
     */
    public
static Map<String, PictureData> getSheetPictrues07(XSSFSheet sheet, XSSFWorkbook workbook) { Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>(); for (POIXMLDocumentPart dr : sheet.getRelations()) { if (dr instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) dr; List<XSSFShape> shapes = drawing.getShapes(); for (XSSFShape shape : shapes) { if (shape instanceof XSSFPicture) { XSSFPicture pic = (XSSFPicture) shape; XSSFClientAnchor clientAnchor = pic.getClientAnchor(); String picIndex = clientAnchor.getRow1() + "_" + clientAnchor.getCol1(); //與原來行號,列號的方法不同但是大同小異 sheetIndexPicMap.put(picIndex, pic.getPictureData()); } } } } return sheetIndexPicMap; }

2、失敗的workbook中還存在校驗成功的圖片

在easypoi匯入資料時可以自定義verifyHandler對資料進行校驗

            ImportParams params = new ImportParams();
            params.setTitleRows(0);
            params.setVerifyHandler((IExcelVerifyHandler<VehicleInfoImportVo>) obj -> {
                try {
                    // 無效資料檢查
                    if (checkInvalid(obj)) {
                        obj.setInvalid(true);
                        return new ExcelVerifyHandlerResult(true);
                    }
                    // 資料格式檢驗
                    String msg = checkVehicleInfoImportVo(obj);
                    if (StringUtils.isBlank(msg)) {
                        return new ExcelVerifyHandlerResult(true);
                    } else {
                        return new ExcelVerifyHandlerResult(false, msg);
                    }
                } catch (Exception e) {
                    log.error("校驗錯誤", e);
                    return new ExcelVerifyHandlerResult(false);
                }
            });
            // 匯入的資料格式
            //objectExcelImportResult 此物件中儲存失敗和成功的資料集合以及workbook
            ExcelImportResult<VehicleInfoImportVo> objectExcelImportResult = ExcelImportUtil.importExcelMore(
                    file.getInputStream(),
                    VehicleInfoImportVo.class, params);

問題的原因(針對於07以上的excel):

在easypoi中,失敗的workbook是通過在原來workbook的基礎上,將正確的資料行刪除,但是沒有對圖片進行刪除所以會導致此問題。

(當時想的比較簡單你沒有刪除我給你刪除就行了,但是在後面的測試中刪除圖片不緊緊是對XSSFPicture進行操作,還需要刪除其中的關係,具體可以看一下POI中插入圖片的程式碼

    private Workbook removeSuperfluousRows(Workbook book, List<Row> rowList, ImportParams params) {
        for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()
                + params.getSheetNum(); i++) {
            for (int j = rowList.size() - 1; j >= 0; j--) {
                if (rowList.get(j).getRowNum() < rowList.get(j).getSheet().getLastRowNum()) {
                    book.getSheetAt(i).shiftRows(rowList.get(j).getRowNum() + 1, rowList.get(j).getSheet().getLastRowNum(), -1);
                } else if (rowList.get(j).getRowNum() == rowList.get(j).getSheet().getLastRowNum()) {
                    book.getSheetAt(i).createRow(rowList.get(j).getRowNum() + 1);
                    book.getSheetAt(i).shiftRows(rowList.get(j).getRowNum() + 1, rowList.get(j).getSheet().getLastRowNum() + 1, -1);
                }
            }
        }
        return book;
    }

解決方案:

在刪除正確的資料行的時候,將對應的照片也刪除

                        Map<String, PictureData> sheetPictrues07 = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(0), (XSSFWorkbook) book);
                        if (sheetPictrues07 != null && sheetPictrues07.size() > 0) {
                            for (Row row : successRow) {
                                int rowNum = row.getRowNum();
                                short lastCellNum = row.getLastCellNum();
                                for (int i = 0; i < lastCellNum; i++) {
                                    PictureData pictureData = sheetPictrues07.get(rowNum + "_" + i);
                                    if (pictureData != null) {
                                        RemovePicWorkbook.removeExcelImageByPicture(book, pictureData);
                                    }
                                }
                            }
                        }
RemovePicWorkbook.java

public class RemovePicWorkbook {
    public static void removeExcelImageByPicture(Workbook workbook, PictureData pictureData) {

        Sheet sheet = workbook.getSheetAt(0);

        Drawing drawing = sheet.getDrawingPatriarch();

        XSSFPicture xssfPictureToDelete = null;

        if (drawing instanceof XSSFDrawing) {

            for (XSSFShape shape : ((XSSFDrawing) drawing).getShapes()) {

                if (shape instanceof XSSFPicture) {

                    XSSFPicture xssfPicture = (XSSFPicture) shape;

                    String shapename = xssfPicture.getShapeName();

                    int row = xssfPicture.getClientAnchor().getRow1();

                    int col = xssfPicture.getClientAnchor().getCol1();

                    if (pictureData instanceof XSSFPictureData) {
                        XSSFPictureData inPictureData = (XSSFPictureData) pictureData;
                        XSSFPictureData curpictureData1 = xssfPicture.getPictureData();
                        PackagePartName inPartName = curpictureData1.getPackagePart().getPartName();
                        PackagePartName curPartName = inPictureData.getPackagePart().getPartName();
                        if (curPartName.equals(inPartName)) xssfPictureToDelete = xssfPicture;
                    }

                }
            }
        }

        if (xssfPictureToDelete != null) ExcelDeleteImage.deleteEmbeddedXSSFPicture(xssfPictureToDelete);

        if (xssfPictureToDelete != null) ExcelDeleteImage.deleteCTAnchor(xssfPictureToDelete);
    }
}
ExcelDeleteImage.java
/**
 * I have now been trying for too long to remove an image from my XSSFSheet. I cannot find any information about this, but I would think that it has to be possible..
 * Is there any way to remove an image from my XSSFSheet? Even the official (?) apache poi website does not mention anything besides creating and reading images
 * I am now not far away from giving up and just copying everything except said image into a new sheet. Which is obviously not how this should be done. I don't think I would be able to sleep well for a week if I did that.
 * My last unsuccessful attempt was to use my code which moves images (I shared that code in this post) but instead of setting valid row numbers I would set null, but that's not possible since the parameter for setRow() is int (primitive type).
 * Then I tried setting a negative value for the anchor rows. While this technically removes the images, the excel file has to be repaired when it is opened the next time. The images are not being displayed.
 * I believe I would have to remove the relation from the XSSFDrawing too to completely remove the image (I think this after finding this custom implementation of XSSFDrawing) but I have no idea what is going on there...
 * I would be grateful for any kind of help here!
 * For XSSF this is not as simple as it sounds. There is HSSFPatriarch.removeShape but there is not something comparable in XSSFDrawing.
 * We must delete the picture itself inclusive the relations. And we must delete the shape's anchor from the drawing.
 * Example which goes trough all pictures in a sheet and deletes a picture if it's shape name is "Image 2":
 */
public class ExcelDeleteImage {

    public static void deleteCTAnchor(XSSFPicture xssfPicture) {

        XSSFDrawing drawing = xssfPicture.getDrawing();

        XmlCursor cursor = xssfPicture.getCTPicture().newCursor();

        cursor.toParent();

        if (cursor.getObject() instanceof org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor) {

            for (int i = 0; i < drawing.getCTDrawing().getTwoCellAnchorList().size(); i++) {

                if (cursor.getObject().equals(drawing.getCTDrawing().getTwoCellAnchorArray(i))) {

                    drawing.getCTDrawing().removeTwoCellAnchor(i);

                    System.out.println("TwoCellAnchor for picture " + xssfPicture + " was deleted.");

                }

            }

        } else if (cursor.getObject() instanceof org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTOneCellAnchor) {

            for (int i = 0; i < drawing.getCTDrawing().getOneCellAnchorList().size(); i++) {

                if (cursor.getObject().equals(drawing.getCTDrawing().getOneCellAnchorArray(i))) {

                    drawing.getCTDrawing().removeOneCellAnchor(i);

                    System.out.println("OneCellAnchor for picture " + xssfPicture + " was deleted.");

                }

            }

        } else if (cursor.getObject() instanceof org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTAbsoluteAnchor) {

            for (int i = 0; i < drawing.getCTDrawing().getAbsoluteAnchorList().size(); i++) {

                if (cursor.getObject().equals(drawing.getCTDrawing().getAbsoluteAnchorArray(i))) {

                    drawing.getCTDrawing().removeAbsoluteAnchor(i);

                    System.out.println("AbsoluteAnchor for picture " + xssfPicture + " was deleted.");

                }

            }

        }

    }

    public static void deleteEmbeddedXSSFPicture(XSSFPicture xssfPicture) {

        if (xssfPicture.getCTPicture().getBlipFill() != null) {

            if (xssfPicture.getCTPicture().getBlipFill().getBlip() != null) {

                if (xssfPicture.getCTPicture().getBlipFill().getBlip().getEmbed() != null) {

                    String rId = xssfPicture.getCTPicture().getBlipFill().getBlip().getEmbed();

                    XSSFDrawing drawing = xssfPicture.getDrawing();

                    drawing.getPackagePart().removeRelationship(rId);

                    drawing.getPackagePart().getPackage().deletePartRecursive(drawing.getRelationById(rId).getPackagePart().getPartName());

                    System.out.println("Picture " + xssfPicture + " was deleted.");

                }

            }

        }

    }

    public static void deleteHSSFShape(HSSFShape shape) {

        HSSFPatriarch drawing = shape.getPatriarch();

        drawing.removeShape(shape);

        System.out.println("Shape " + shape + " was deleted.");

    }

    public static void main(String[] args) throws Exception {

        String filename = "C:\\Users\\yuxia\\Desktop\\ce.xlsx";

        String outfilename = "C:\\Users\\yuxia\\Desktop\\ce1.xlsx";

        InputStream inp = new FileInputStream(filename);

        Workbook workbook = WorkbookFactory.create(inp);

        Sheet sheet = workbook.getSheetAt(0);

        Drawing drawing = sheet.getDrawingPatriarch();

        XSSFPicture xssfPictureToDelete = null;

        if (drawing instanceof XSSFDrawing) {

            for (XSSFShape shape : ((XSSFDrawing) drawing).getShapes()) {

                if (shape instanceof XSSFPicture) {
                    XSSFPicture xssfPicture = (XSSFPicture) shape;

                    String shapename = xssfPicture.getShapeName();

                    int row = xssfPicture.getClientAnchor().getRow1();

                    int col = xssfPicture.getClientAnchor().getCol1();

                    System.out.println("Picture " + "" + " with Shapename: " + shapename + " is located row: " + row + ", col: " + col);

                    if ("圖片 3".equals(shapename)) xssfPictureToDelete = xssfPicture;

                }

            }

        }

        if (xssfPictureToDelete != null) deleteEmbeddedXSSFPicture(xssfPictureToDelete);

        if (xssfPictureToDelete != null) deleteCTAnchor(xssfPictureToDelete);

//        HSSFPicture hssfPictureToDelete = null;
//
//        if (drawing instanceof HSSFPatriarch) {
//
//            for (HSSFShape shape : ((HSSFPatriarch) drawing).getChildren()) {
//
//                if (shape instanceof HSSFPicture) {
//
//                    HSSFPicture hssfPicture = (HSSFPicture) shape;
//
//                    int picIndex = hssfPicture.getPictureIndex();
//
//                    String shapename = hssfPicture.getShapeName().trim();
//
//                    int row = hssfPicture.getClientAnchor().getRow1();
//
//                    int col = hssfPicture.getClientAnchor().getCol1();
//
//                    System.out.println("Picture " + picIndex + " with Shapename: " + shapename + " is located row: " + row + ", col: " + col);
//
//                    if ("Image 2".equals(shapename)) hssfPictureToDelete = hssfPicture;
//
//                }
//
//            }
//
//        }
//
//        if (hssfPictureToDelete != null) deleteHSSFShape(hssfPictureToDelete);

        FileOutputStream out = new FileOutputStream(outfilename);

        workbook.write(out);

        out.close();

        workbook.close();

    }

}
ExcelDeleteImage中的程式碼來源於外網
*************************************************************************************************************************
此外在匯入excel時easypoi中只支援將圖片儲存到一個目錄下,此問題可以通過重寫ExcelImportService方法來解決
    private void saveImage(Object object, String picId, Map<String, ExcelImportEntity> excelParams,
                           String titleString, Map<String, PictureData> pictures,
                           ImportParams params) throws Exception {
        if (pictures == null) {
            return;
        }
        PictureData image = pictures.get(picId);
        if (image == null) {
            return;
        }
        byte[] data = image.getData();
        String fileName = IdUtils.fastUUID();
        fileName += "." + PoiPublicUtil.getFileExtendName(data);
        if (excelParams.get(titleString).getSaveType() == 1) {
            String path = getSaveUrl(); // 此方法獲取儲存的路徑擴充套件一下就好了
            File savefile = new File(path);
            if (!savefile.exists()) {
                savefile.mkdirs();
            }
            savefile = new File(path + "/" + fileName);
            FileOutputStream fos = new FileOutputStream(savefile);
            try {
                fos.write(data);
            } finally {
                IOUtils.closeQuietly(fos);
            }
            setValues(excelParams.get(titleString), object,
                    getAbsoluteSaveUrl(path) + "/" + fileName);
            pictures.remove(picId);
        } else {
            setValues(excelParams.get(titleString), object, data);
        }
    }