easypoi(基於poi)導入數據以及模板導出數據遇到的問題以及部分解決方式


項目在直接使用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);
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM