Apache poi : setting custom properties at worksheet level using apache poi

假装没事ソ 提交于 2020-12-27 05:54:29

问题


public static void main(String[] args) {
  try {
    FileInputStream file = new FileInputStream(new File("D://New Microsoft Excel Worksheet.xlsx"));
    XSSFWorkbook wb = new XSSFWorkbook(file);
    XSSFSheet sheet = wb.createSheet("newsheet5");
    CTWorksheet ctSheet = sheet.getCTWorksheet();

    CTCustomProperties props = ctSheet.addNewCustomProperties();
    props.addNewCustomPr().setId("APACHE POI");
    props.addNewCustomPr().setName("Tender no = 48");
    props.addNewCustomPr().setId("APACHE POI 2");
    props.addNewCustomPr().setName("tender no = 58");
    ctSheet.setCustomProperties(props);

    FileOutputStream out = new FileOutputStream("D://New Microsoft Excel Worksheet.xlsx");
    wb.write(out);
    out.close();
    wb.close();
  } catch (Exception e) {
    e.printStackTrace();
  } 
}

Xlsx file is corrupted after writing custom properties at sheet level.

I'm getting an error message as 'excel cannot open the file because the file format or file extension is not valid . Vefiry that the file has not been corrupted and the file extension matches the format of the file' when tried open the excel file.


回答1:


Sheet custom properties only are useable using VBA. They are stored in the Excel file but the values are within binary document parts customProperty1.bin, customProperty2.bin, ... This is nothing what apache poi provides access to until now.

Using XSSF one needs creating the binary document part, then getting the relation Id to that binary document part. Then set CTCustomProperties - CTCustomProperty. There the Id points to the binary document part containing the value and the name is the property name.

Following complete example shows this. It is tested and works using current apache poi 4.1.2. It needs ooxml-schemas-1.4.jar being in class path because default poi-ooxml-schemas-4.1.2.jar does not contain all needed low level CT*-classes.

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import org.apache.poi.xssf.usermodel.*;

import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;

import org.apache.poi.openxml4j.opc.*;
import org.apache.poi.ooxml.POIXMLDocumentPart;

import java.nio.charset.StandardCharsets;

class CreateExcelSheetCustomProperties {

 static void setSheetCustomProperty(XSSFSheet sheet, String customPropertyName, String customPropertyValue) throws Exception {

  OPCPackage opcpackage = sheet.getWorkbook().getPackage();
  int i = opcpackage.getUnusedPartIndex("/customProperty#.bin");
  PackagePartName partname = PackagingURIHelper.createPartName("/customProperty" + i + ".bin");
  PackagePart part = opcpackage.createPart(partname, "application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty");
  POIXMLDocumentPart customProperty = new POIXMLDocumentPart(part) {
   @Override
   protected void commit() throws IOException {
    PackagePart part = getPackagePart();
    OutputStream out = part.getOutputStream();
    try {
     out.write(customPropertyValue.getBytes(StandardCharsets.UTF_16LE));
     out.close();
    } catch (Exception ex) {
     ex.printStackTrace();
    }; 
   }
  };

  String rId = sheet.addRelation(null, XSSFRelation.CUSTOM_PROPERTIES, customProperty).getRelationship().getId();
  
  CTWorksheet ctSheet = sheet.getCTWorksheet();
  CTCustomProperties props = ctSheet.getCustomProperties();
  if (props == null) props = ctSheet.addNewCustomProperties();
  CTCustomProperty prop = props.addNewCustomPr();
  prop.setId(rId);
  prop.setName(customPropertyName);
 }

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

  try (XSSFWorkbook workbook = new XSSFWorkbook(); 
       FileOutputStream fileout = new FileOutputStream("./Excel.xlsx") ) {

   XSSFSheet sheet = workbook.createSheet();

   setSheetCustomProperty(sheet, "APACHE POI", "Tender no = 48");
   setSheetCustomProperty(sheet, "APACHE POI 2", "tender no = 58");

   workbook.write(fileout);
  }
 }
}



回答2:


I've been struggling with the same issue and found a way to make it work, but it's far from optimal. Here it is anyway and hopefully you or someone else can come up with a better method.

package temp.temp;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCustomProperties;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCustomProperty;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;

public class Temp2 {

    public static void main(String[] args) {
        File inputFile = new File("C:\\myspreadsheet.xlsx");
        try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream(inputFile))) {
            XSSFWorkbook wb = new XSSFWorkbook(fis); 
            
            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
                XSSFSheet sheet  = wb.getSheetAt(i);
                System.out.println("\nSheetName=" + sheet.getSheetName());
                
                CTWorksheet ctSheet = sheet.getCTWorksheet();
                CTCustomProperties props = ctSheet.getCustomProperties();
                
                if (props != null) {
                    List<CTCustomProperty> propList = props.getCustomPrList();
                    propList.stream().forEach((prop) -> {
                        POIXMLDocumentPart rel = sheet.getRelationById(prop.getId());
                        if (rel != null) {
                            try (InputStream inp  = rel.getPackagePart().getInputStream()) {
                                byte[] inBytes = inp.readAllBytes();
                                // By experimentation, byte array has two bytes per character with least 
                                //  significant in first byte which is UTF-16LE encoding.  Don't know why!
                                String value = new String(inBytes, "UTF-16LE");
                                System.out.println("   " + prop.getName() + "=" + value);
                            } catch (IOException ioe) {
                                //Error
                            }
                        }
                    }); 
                }
                
            
            }
            wb.close();
        } catch (Exception e) {
            System.out.println(e);
        }
        System.out.println("End");
    }
}

Note that CTWorksheet comes from poi-ooxml-schemas-xx.jar and CustomProperties from ooxml-schemas-yy.jar, so both have to be on the classpath. If you're using modules (as I am), this gives big problems! Good Luck



来源:https://stackoverflow.com/questions/65320472/apache-poi-setting-custom-properties-at-worksheet-level-using-apache-poi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!