2016-11-02 12 views
4

Я нашел kiwiwingsanswer на вопрос о том, как вы можете встраивать файлы в Excel с помощью Apache POI, но, к сожалению, его ответ охватывает только таблицы HSSF (формат XLS), и мы в настоящее время используют новый формат XSSF (XLSX), а решение, предлагаемое для электронных таблиц HSSF, не будет работать. Я попытался портировать его, но последний гвоздь в гробу исходит из того факта, что в мире XSSF нет эквивалента HSSFObjectData.Вставить файлы в листы XSSF в Excel, используя Apache POI

Это то, что я сделал до сих пор - я нашел способ прикреплять файлы к файлу Excel. Этот код делает это:

private PackagePart packageNotebook(
    final OPCPackage pkg, 
    final String notebookTable, 
    final String taskId, 
    final String notebookName, 
    final byte[] contents 
) throws InvalidFormatException, IOException 
{ 
    final PackagePartName partName = 
     PackagingURIHelper.createPartName("/notebook/" + notebookTable + "/" + taskId + "/" + notebookName); 
    pkg.addRelationship(partName, TargetMode.INTERNAL, PackageRelationshipTypes.CUSTOM_XML); 
    final PackagePart part = pkg.createPart(partName, "text/xml"); 
    IOUtils.write(contents, part.getOutputStream()); 

    return part; 
} 

Я также был в состоянии создать образ, который я хотел использовать в качестве якоря в файле Excel. Однако то, что я не могу сделать, - это «связать» этот образ со встроенным контентом, как это делали киви, в его ответе.

Моя конечная цель состоит в том, чтобы иметь файл XLSX Excel со встроенными объектами в нем, таким образом, чтобы пользователь мог дважды щелкнуть в ядре, который я открываю в ячейках, а затем иметь возможность редактировать файл, как и вы если вы вложили файл с помощью клиента Excel.

У кого-нибудь есть рабочий пример о том, как это сделать?

+0

Привет @ ravi-wallau, вы можете найти in-progress paste @ http://pastebin.com/mgh8nevF Работает в Excel 2016, но не в Excel-Viewer (потому что я не реализовал чертежи VML), а также не в Libre Office. Но мой оригинальный образец файла Excel2016 также не работал в Libre Office – kiwiwings

+0

Хорошо, что сейчас работает pastebin, т.е. excel viewer больше не жалуется, но он все еще не отображается из-за пропущенных рисунков vml ... но к моему смущению Я понял, что я реализовал внедрение других офисных документов, тогда как вы хотели просто включить произвольные файлы ...: S – kiwiwings

+0

@kiwiwings Я проверю это сейчас! –

ответ

1

По сравнению с HSSF/Package Manager stuff было выполнено более прямолинейно. :)

Как обычно, я начал с создания необходимого файла через Excel 2016 и проверил, что находится внутри xmls. Офису нравится ставить много AlternateContent tags in - в нижеследующем решении я удалил эти обертки и напрямую предоставил элементы в исходных элементах «Выбор», по крайней мере, с помощью Excel2016 он работает ... - имейте в виду, что вложения в оригинальные файлы Excel2016 не могут быть открыты в Libre Office 5/Excel-Viewer, поэтому вашим пользователям нужна нормальная установка.

Поскольку я реализовал его в полной базе кода разработчика POI, вам может понадобиться использовать full schemas.

Изображения предварительного просмотра будут заменены, когда пользователи попытаются открыть встроенные объекты. Было бы неплохо, если бы POI-пакеты WMF могли использоваться для генерации изображений предварительного просмотра «на лету», но я только реализовал их только для чтения :(

Если встроенные элементы не могут быть открыты, пожалуйста, оставьте меня линия ваших пользователей установки офиса, и я пытаюсь принизить соответственно.

package org.apache.poi.xssf; 

import java.awt.geom.Rectangle2D; 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.util.HashSet; 
import java.util.Set; 

import javax.xml.namespace.QName; 

import org.apache.poi.POIXMLDocument; 
import org.apache.poi.hpsf.ClassID; 
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 
import org.apache.poi.openxml4j.opc.PackagePart; 
import org.apache.poi.openxml4j.opc.PackagePartName; 
import org.apache.poi.openxml4j.opc.PackageRelationship; 
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; 
import org.apache.poi.openxml4j.opc.PackagingURIHelper; 
import org.apache.poi.openxml4j.opc.TargetMode; 
import org.apache.poi.poifs.filesystem.Ole10Native; 
import org.apache.poi.poifs.filesystem.Ole10NativeException; 
import org.apache.poi.poifs.filesystem.POIFSFileSystem; 
import org.apache.poi.ss.usermodel.Workbook; 
import org.apache.poi.xslf.usermodel.XMLSlideShow; 
import org.apache.poi.xslf.usermodel.XSLFTextBox; 
import org.apache.poi.xssf.usermodel.XSSFClientAnchor; 
import org.apache.poi.xssf.usermodel.XSSFDrawing; 
import org.apache.poi.xssf.usermodel.XSSFPicture; 
import org.apache.poi.xssf.usermodel.XSSFRelation; 
import org.apache.poi.xssf.usermodel.XSSFSheet; 
import org.apache.poi.xssf.usermodel.XSSFWorkbook; 
import org.apache.xmlbeans.XmlCursor; 
import org.apache.xmlbeans.XmlException; 
import org.apache.xmlbeans.XmlObject; 
import org.junit.Test; 
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtension; 
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtensionList; 
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture; 
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObjects; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; 

public class TestEmbed { 
    static final String drawNS = "http://schemas.microsoft.com/office/drawing/2010/main"; 
    static final String relationshipsNS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; 

    // write some embedded objects to sheet 
    @Test 
    public void write() throws IOException, InvalidFormatException { 
     XSSFWorkbook wb = new XSSFWorkbook(); 
     XSSFSheet sh = wb.createSheet(); 

     int imgPptId = addImageToWorkbook(wb, "ppt-icon.jpg", Workbook.PICTURE_TYPE_JPEG); 
     int imgPckId = addImageToWorkbook(wb, "PackageIcon.png", Workbook.PICTURE_TYPE_PNG); 

     String imgPckRelId = addImageToSheet(sh, imgPckId, Workbook.PICTURE_TYPE_PNG); 
     String imgPptRelId = addImageToSheet(sh, imgPptId, Workbook.PICTURE_TYPE_JPEG); 

     // embed two different HTML pages via package manager 
     XSSFClientAnchor imgAnchor1 = new XSSFClientAnchor(0, 0, 0, 0, 1, 1, 3, 3); 
     String oleRelId1 = addHtml(sh, 1); 
     int shapeId1 = addImageToShape(sh, imgAnchor1, imgPckId); 
     addObjectToShape(sh, imgAnchor1, shapeId1, oleRelId1, imgPckRelId, "Objekt-Manager-Shellobjekt"); 

     XSSFClientAnchor imgAnchor2 = new XSSFClientAnchor(0, 0, 0, 0, 5, 1, 7, 3); 
     String oleRelId2 = addHtml(sh, 2); 
     int shapeId2 = addImageToShape(sh, imgAnchor2, imgPckId); 
     addObjectToShape(sh, imgAnchor2, shapeId2, oleRelId2, imgPckRelId, "Objekt-Manager-Shellobjekt"); 

     // embed a slideshow (no package manager needed) 
     XSSFClientAnchor imgAnchor3 = new XSSFClientAnchor(0, 0, 0, 0, 1, 5, 7, 10); 
     String oleRelId3 = addSlideShow(sh, 1); 
     int shapeId3 = addImageToShape(sh, imgAnchor3, imgPptId); 
     addObjectToShape(sh, imgAnchor3, shapeId3, oleRelId3, imgPptRelId, "Presentation"); 


     FileOutputStream fos = new FileOutputStream("bla.xlsx"); 
     wb.write(fos); 
     fos.close(); 

     wb.close(); 
    } 

    // read Ole10Native objects from workbook 
    @Test 
    public void read() throws IOException, XmlException, Ole10NativeException { 
     XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream("bla.xlsx")); 
     XSSFSheet sheet = wb.getSheetAt(0); 
     CTWorksheet cws = sheet.getCTWorksheet(); 
     if (!cws.isSetOleObjects()) { 
      System.out.println("sheet has no ole objects"); 
     } else { 
      Set<Integer> processedShapes = new HashSet<Integer>(); 
      for (XmlObject xOleObj : cws.getOleObjects().selectPath("declare namespace p='"+XSSFRelation.NS_SPREADSHEETML+"' .//p:oleObject")) { 
       XmlCursor cur = xOleObj.newCursor(); 
       String shapeId = cur.getAttributeText(new QName("shapeId")); 
       String relId = cur.getAttributeText(new QName(relationshipsNS, "id")); 
       cur.dispose(); 

       if (processedShapes.contains(Integer.valueOf(shapeId))) { 
        continue; 
       } 
       processedShapes.add(Integer.valueOf(shapeId)); 

       PackagePart pp = sheet.getRelationById(relId).getPackagePart(); 
       if ("application/vnd.openxmlformats-officedocument.oleObject".equals(pp.getContentType())) { 
        InputStream is = pp.getInputStream(); 
        POIFSFileSystem poifs = new POIFSFileSystem(is); 
        is.close(); 
        Ole10Native ole10 = Ole10Native.createFromEmbeddedOleObject(poifs); 
        poifs.close(); 
        System.out.println("Filename: "+ole10.getFileName()+" - content length: "+ole10.getDataSize()); 
       } 
      } 

     } 
     wb.close(); 
    } 



    // add a dummy html to the embeddings folder 
    private static String addHtml(XSSFSheet sh, int oleId) throws IOException, InvalidFormatException { 
     String html10 = "<html><body><marquee>This is the end. Html-id: "+oleId+"</marquee></body></html>"; 
     Ole10Native ole10 = new Ole10Native("html"+oleId+".html", "html"+oleId+".html", "html"+oleId+".html", html10.getBytes("ISO-8859-1")); 

     ByteArrayOutputStream bos = new ByteArrayOutputStream(500); 
     ole10.writeOut(bos); 

     POIFSFileSystem poifs = new POIFSFileSystem(); 
     poifs.getRoot().createDocument(Ole10Native.OLE10_NATIVE, new ByteArrayInputStream(bos.toByteArray())); 

     poifs.getRoot().setStorageClsid(ClassID.OLE10_PACKAGE); 


     final PackagePartName pnOLE = PackagingURIHelper.createPartName("/xl/embeddings/oleObject"+oleId+".bin"); 
     final PackagePart partOLE = sh.getWorkbook().getPackage().createPart(pnOLE, "application/vnd.openxmlformats-officedocument.oleObject"); 
     PackageRelationship prOLE = sh.getPackagePart().addRelationship(pnOLE, TargetMode.INTERNAL, POIXMLDocument.OLE_OBJECT_REL_TYPE); 
     OutputStream os = partOLE.getOutputStream(); 
     poifs.writeFilesystem(os); 
     os.close(); 
     poifs.close(); 

     return prOLE.getId(); 
    } 


    // add a dummy slideshow to the embeddings folder 
    private static String addSlideShow(XSSFSheet sh, int pptId) throws IOException, InvalidFormatException { 
     XMLSlideShow ppt = new XMLSlideShow(); 
     XSLFTextBox tb = ppt.createSlide().createTextBox(); 
     tb.setText("this is the end - PPT-ID: "+pptId); 
     tb.setAnchor(new Rectangle2D.Double(100,100,100,100)); 

     final PackagePartName pnPPT = PackagingURIHelper.createPartName("/xl/embeddings/sample"+pptId+".pptx"); 
     final PackagePart partPPT = sh.getWorkbook().getPackage().createPart(pnPPT, "application/vnd.openxmlformats-officedocument.presentationml.presentation"); 
     PackageRelationship prPPT = sh.getPackagePart().addRelationship(pnPPT, TargetMode.INTERNAL, POIXMLDocument.PACK_OBJECT_REL_TYPE); 
     OutputStream os = partPPT.getOutputStream(); 
     ppt.write(os); 
     os.close(); 
     ppt.close(); 

     return prPPT.getId(); 
    } 



    private static int addImageToWorkbook(XSSFWorkbook wb, String fileName, int pictureType) throws IOException { 
     FileInputStream fis = new FileInputStream(fileName); 
     int imgId = wb.addPicture(fis, pictureType); 
     fis.close(); 
     return imgId; 
    } 

    private static String addImageToSheet(XSSFSheet sh, int imgId, int pictureType) throws InvalidFormatException { 
     final PackagePartName pnIMG = PackagingURIHelper.createPartName("/xl/media/image"+(imgId+1)+(pictureType == Workbook.PICTURE_TYPE_PNG ? ".png" : ".jpeg")); 
     PackageRelationship prIMG = sh.getPackagePart().addRelationship(pnIMG, TargetMode.INTERNAL, PackageRelationshipTypes.IMAGE_PART); 
     return prIMG.getId(); 
    } 


    private static int addImageToShape(XSSFSheet sh, XSSFClientAnchor imgAnchor, int imgId) { 
     XSSFDrawing pat = sh.createDrawingPatriarch(); 
     XSSFPicture pic = pat.createPicture(imgAnchor, imgId); 

     CTPicture cPic = pic.getCTPicture(); 
     int shapeId = (int)cPic.getNvPicPr().getCNvPr().getId(); 
     cPic.getNvPicPr().getCNvPr().setHidden(true); 
     CTOfficeArtExtensionList extLst = cPic.getNvPicPr().getCNvPicPr().addNewExtLst(); 
     // https://msdn.microsoft.com/en-us/library/dd911027(v=office.12).aspx 
     CTOfficeArtExtension ext = extLst.addNewExt(); 
     ext.setUri("{63B3BB69-23CF-44E3-9099-C40C66FF867C}"); 
     XmlCursor cur = ext.newCursor(); 
     cur.toEndToken(); 
     cur.beginElement(new QName(drawNS, "compatExt", "a14")); 
     cur.insertAttributeWithValue("spid", "_x0000_s"+shapeId); 


     return shapeId; 
    } 



    private static void addObjectToShape(XSSFSheet sh, XSSFClientAnchor imgAnchor, int shapeId, String objRelId, String imgRelId, String progId) { 
     CTWorksheet cwb = sh.getCTWorksheet(); 
     CTOleObjects oo = cwb.isSetOleObjects() ? cwb.getOleObjects() : cwb.addNewOleObjects(); 

     CTOleObject ole1 = oo.addNewOleObject(); 
     ole1.setProgId(progId); 
     ole1.setShapeId(shapeId); 
     ole1.setId(objRelId); 


     XmlCursor cur1 = ole1.newCursor(); 
     cur1.toEndToken(); 
     cur1.beginElement("objectPr", XSSFRelation.NS_SPREADSHEETML); 
     cur1.insertAttributeWithValue("id", relationshipsNS, imgRelId); 
     cur1.insertAttributeWithValue("defaultSize", "0"); 
     cur1.beginElement("anchor", XSSFRelation.NS_SPREADSHEETML); 
     cur1.insertAttributeWithValue("moveWithCells", "1"); 

     CTTwoCellAnchor anchor = CTTwoCellAnchor.Factory.newInstance(); 
     anchor.setFrom(imgAnchor.getFrom()); 
     anchor.setTo(imgAnchor.getTo()); 

     XmlCursor cur2 = anchor.newCursor(); 
     cur2.copyXmlContents(cur1); 
     cur2.dispose(); 

     cur1.toParent(); 
     cur1.toFirstChild(); 
     cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "from")); 
     cur1.toNextSibling(); 
     cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "to")); 

     cur1.dispose(); 
    } 
} 
+0

Спасибо @kiwiwings, это сработало. –

3

Я применил патч через #60586, поэтому вложение теперь намного проще. Ниже snipplet берется из corresponding JUnit test.

Workbook wb1 = new XSSFWorkbook(); 
Sheet sh = wb1.createSheet(); 
int picIdx = wb1.addPicture(getSamplePng(), Workbook.PICTURE_TYPE_PNG); 
byte samplePPTX[] = getSamplePPT(true); 
int oleIdx = wb1.addOlePackage(samplePPTX, "dummy.pptx", "dummy.pptx", "dummy.pptx"); 

Drawing<?> pat = sh.createDrawingPatriarch(); 
ClientAnchor anchor = pat.createAnchor(0, 0, 0, 0, 1, 1, 3, 6); 
pat.createObjectData(anchor, oleIdx, picIdx); 
+0

Я создал ошибку https: //bz.apache.org/bugzilla/show_bug.cgi? id = 62017 для проблемы, которую я обнаружил при ее тестировании - она ​​терпит неудачу, когда определенное количество объектов OLE добавляется к листу. –

Смежные вопросы