使用Apache POI将文件嵌入到Excel中 [英] Embed files into Excel using Apache POI

查看:1241
本文介绍了使用Apache POI将文件嵌入到Excel中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Apache POI将数据导出到Excel文件。在一个奇怪的要求中,我需要使用这个POI在Excel中嵌入一个文件。我有文件,可以采用流或字节数组。经过很多时间的搜索之后,我怀疑POI是否真的支持我的要求。我们可以将文件嵌入到Excel中吗? : - (



Cheers,
Anoop

解决方案

好的,这需要很长时间才能终于解决,因为有一些事情在开始时看起来不是非常重要,但是实际上在文件设置不正确的时候损坏了文件 - 特别是在Ole10Native包装器中,部分未知2字段实际上包含以下命令字符串的大小(以字节为单位)。



但首先要做的是:



当您想将任意文件嵌入其中一种Office格式时,最好的方法是使用OLE 1.0打包程序,当您从文件中选择insert-> object时,通常会使用它。



所以我重新设计了一个包含PPT的Excel 2003文件,如上面我的评论所述, Excel将存储其嵌入对象 DirectoryNodes 命名为MBD ....。在Ole 1.0 Packager对象的情况下,有趣的数据将在 \1Ole10Native 条目中找到。



当您插入数据时,您需要将其以Excel表格的形式进行链接。这是由类似于附加附加记录的图片条目的EscherObject完成的。​​



除了许多无证标志之外,还有一些让我困惑的事情:




  • 是随机分配的嵌入对象的存储ID,还是有某种数字系统?

  • 我搜索了Ole10Native包装器的更详细的描述,特别是对于ole 1.0 packager格式,除了 M $ docu ,它大致处理它作为一个大字节块,大多数来源做了一些重新设计,看起来非常类似于 poi Ole10Native class ...当然,检查libre办公室来源的想法也在想,但我有承认我检查的那些,只会让我感到困惑:(

  • 哪一个是r对于嵌入对象来说, ...对于powerpoint来说,有很多。所以如果有疑问,很明显你需要从Office

  • 中查找以前保存的文件的clsid。Excel 2010生成Biff8文件,Libre Office!无法打开嵌入的对象! !

  • ole10Native对象包含一个命令行条目。如果有人可以从嵌入式对象开始其他的东西,那么这将是有趣的...

  • BiffViewer 在使用比某些大块(〜6kb)大的预览图像时,会崩溃。所以任何一个图像都需要被分块或BiffViewer的实现是错误的...这也造成一些混乱一段时间...



使用POI 3.9测试,Libre Office 4.0,Office 2010(我没有Office 2003已经...)

  import java.awt.Color中; 
import java.io. *;
import java.lang.reflect。*;
import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.filechooser.FileSystemView;

import org.apache.poi.ddf。*;
import org.apache.poi.hpsf.ClassID;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.model。*;
import org.apache.poi.hslf.model.ShapeTypes;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hssf.dev.BiffViewer;
import org.apache.poi.hssf.model。*;
import org.apache.poi.hssf.record。*;
import org.apache.poi.hssf.usermodel。*;
import org.apache.poi.poifs.filesystem。*;
import org.apache.poi.ss.usermodel。*;
import org.apache.poi.util。*;

@SuppressWarnings(unused)
public class PoiOlePptInXls {

public static final OleType PACKAGE = new OleType({0003000C-0000-0000-C000- 000000000046});
public static final OleType PPT_SHOW = new OleType({64818D10-4F9B-11CF-86EA-00AA00B929E8});
public static final OleType XLS_WORKBOOK = new OleType({00020841-0000-0000-C000-000000000046});
public static final OleType TXT_ONLY = new OleType({5e941d80-bf96-11cd-b579-08002b30bfeb}); // ???

static class OleType {
final String classId;
OleType(String classId){
this.classId = classId;
}
ClassID getClassID(){
ClassID cls = new ClassID();
byte clsBytes [] = cls.getBytes();
String clsStr = classId.replaceAll([{} - ],); (int i = 0; i clsBytes [i / 2] =(byte)Integer.parseInt(clsStr.substring(i,i + 2),16);
}
return cls;
}
}

public static void main(String [] args)throws Exception {
POIFSFileSystem poifs = new POIFSFileSystem();

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();

int previewIdxPpt = generatePreview(wb,application / powerpoint);
int storageIdPpt = packageOleData(poifs,getSamplePPT(),Example.ppt,Example.ppt,Example.ppt);
int previewIdxXls = generatePreview(wb,application / excel);
int storageIdXls = packageOleData(poifs,getSampleXLS(),Example.xls,Example.xls,Example.xls);
int previewIdxTxt = generatePreview(wb,text / plain);
int storageIdTxt = packageOleData(poifs,getSampleTXT(),Example.txt,Example.txt,Example.txt);

int rowoffset = 5;
int coloffset = 5;

CreationHelper ch = wb.getCreationHelper();
HSSFClientAnchor anchor =(HSSFClientAnchor)ch.createClientAnchor();
anchor.setAnchor((short)(2 + coloffset),1 + rowoffset,0,0,(short)(3 + coloffset),5 + rowoffset,0,0)
anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

HSSFObjectData oleShape = createObjectData(poifs,storageIdPpt,1,anchor,previewIdxPpt);
addShape(主族,oleShape);

anchor =(HSSFClientAnchor)ch.createClientAnchor();
anchor.setAnchor((short)(5 + coloffset),1 + rowoffset,0,0,(short)(6 + coloffset),5 + rowoffset,0,0);
anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

oleShape = createObjectData(poifs,storageIdXls,2,anchor,previewIdxXls);
addShape(主族,oleShape);

anchor =(HSSFClientAnchor)ch.createClientAnchor();
anchor.setAnchor((short)(3 + coloffset),10 + rowoffset,0,0,(short)(5 + coloffset),11 + rowoffset,0,0)
anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

oleShape = createObjectData(poifs,storageIdTxt,3,anchor,previewIdxTxt);
addShape(主族,oleShape);

anchor =(HSSFClientAnchor)ch.createClientAnchor();
anchor.setAnchor((short)(1 + coloffset),-2 + rowoffset,0,0,(short)(7 + coloffset),14 + rowoffset,0,0)
anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

HSSFSimpleShape circle = patriarch.createSimpleShape(anchor);
circle.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
circle.setNoFill(true);

poifs.getRoot()。createDocument(Workbook,新的ByteArrayInputStream(wb.getBytes()));

FileOutputStream fos = new FileOutputStream(ole_ppt_in_xls.xls);
poifs.writeFilesystem(fos);
fos.close();
}

static void addShape(HSSFPatriarch主族,HSSFShape形状)throws异常{
patriarch.addShape(shape);
方法m = HSSFPatriarch.class.getDeclaredMethod(onCreate,HSSFShape.class);
m.setAccessible(true);
m.invoke(主族,形状);
}

static HSSFObjectData createObjectData(POIFSFileSystem poifs,int storageId,int objectIdx,HSSFClientAnchor anchor,int previewIdx){
ObjRecord obj = new ObjRecord();
CommonObjectDataSubRecord ftCmo = new CommonObjectDataSubRecord();
ftCmo.setObjectType(CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE);
ftCmo.setObjectId(objectIdx);
ftCmo.setLocked(true);
ftCmo.setPrintable(true);
ftCmo.setAutofill(true);
ftCmo.setAutoline(true);
ftCmo.setReserved1(0);
ftCmo.setReserved2(0);
ftCmo.setReserved3(0);
obj.addSubRecord(ftCmo);

obj.addSubRecord(SubRecord.createSubRecord(new LittleEndianByteArrayInputStream(new byte [] {7,0,2,0,2,0}),0));
obj.addSubRecord(SubRecord.createSubRecord(new LittleEndianByteArrayInputStream(new byte [] {8,0,2,0,1,0}),0));

EmbeddedObjectRefSubRecord ftPictFmla;
try {
构造函数< EmbeddedObjectRefSubRecord> con = EmbeddedObjectRefSubRecord.class.getDeclaredConstructor();
con.setAccessible(true);
ftPictFmla = con.newInstance();
} catch(Exception e){
throw new RuntimeException(oops,e);
}

setField(ftPictFmla,field_2_unknownFormulaData,新字节[] {2,0,0,0,0});
setField(ftPictFmla,field_4_ole_classname,Paket);
setField(ftPictFmla,field_5_stream_id,(Integer)storageId);

obj.addSubRecord(ftPictFmla);
obj.addSubRecord(new EndSubRecord());

//创建临时图片,但不要附加它。
//创建sp-container是必需的,需要最小化修改
// for oleshapes

HSSFPicture shape = new HSSFPicture(null,anchor);
EscherContainerRecord spContainer;

try {
方法m = HSSFPicture.class.getDeclaredMethod(createSpContainer);
m.setAccessible(true);
spContainer =(EscherContainerRecord)m.invoke(shape);
} catch(Exception e){
throw new RuntimeException(oops,e);
}

EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
spRecord.setFlags(spRecord.getFlags()| EscherSpRecord.FLAG_OLESHAPE);
spRecord.setShapeType((byte)0x4B);
EscherOptRecord optRecord = spContainer.getChildById(EscherOptRecord.RECORD_ID);

EscherProperty ep = new EscherSimpleProperty(EscherProperties.BLIP__PICTUREID,false,false,1);
optRecord.addEscherProperty(ep);

DirectoryEntry oleRoot;
尝试{
oleRoot =(DirectoryEntry)poifs.getRoot()。getEntry(formatStorageId(storageId));
} catch(FileNotFoundException e){
throw new RuntimeException(oops,e);
}
HSSFObjectData oleShape = new HSSFObjectData(spContainer,obj,oleRoot);
oleShape.setPictureIndex(previewIdx);
return oleShape;
}

static void setField(Object clazz,String fieldname,Object value){
try {
Field f = clazz.getClass()。getDeclaredField(fieldname) ;
f.setAccessible(true);
f.set(clazz,value);
} catch(Exception e){
throw new RuntimeException(oops,e);
}
}

static void addOleStreamEntry(DirectoryEntry dir)throws IOException {
final String OLESTREAM_NAME =\\\Ole;
如果(!dir.hasEntry(OLESTREAM_NAME)){
//以下数据是从一个例子中获取的libre office文档
//在这个\\\Ole记录旁边还有其他几个记录,例如CompObj,
// OlePresXXX,但是似乎他们不是必需的
byte oleBytes [] = {1,0,0,2,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0};
dir.createDocument(OLESTREAM_NAME,新的ByteArrayInputStream(oleBytes));
}
}

static String formatStorageId(int storageId){
return String.format(MBD%1 $ 08X,storageId);
}

static int packageOleData(POIFSFileSystem poifs,byte oleData [],String label,String fileName,String command)throws IOException {
DirectoryNode root = poifs.getRoot();
//获取免费MBD-Node
int storageId = 0;
DirectoryEntry oleDir = null;
do {
String storageStr = formatStorageId(++ storageId);
if(!root.hasEntry(storageStr)){
oleDir = root.createDirectory(storageStr);
oleDir.setStorageClsid(PACKAGE.getClassID());
}
} while(oleDir == null);

addOleStreamEntry(oleDir);

Ole10Native2 oleNative = new Ole10Native2();
oleNative.setLabel(label);
oleNative.setFileName(fileName);
oleNative.setCommand(command);
oleNative.setDataBuffer(oleData); bb = bte =
oleNative.writeOut(bos);
byte buf1 [] = bos.toByteArray();

oleDir.createDocument(Ole10Native2.OLE10_NATIVE,new ByteArrayInputStream(buf1));

return storageId;
}

static byte [] getSamplePPT(){
HSLFSlideShow ss = HSLFSlideShow.create();
SlideShow ppt = new SlideShow(ss);
幻灯片幻灯片= ppt.createSlide();

AutoShape sh1 = new AutoShape(ShapeTypes.Star32);
sh1.setAnchor(new java.awt.Rectangle(50,50,100,200));
sh1.setFillColor(Color.red);
slide.addShape(sh1); bb = bte =
try {
ppt.write(bos);

POIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
poifs.getRoot()。setStorageClsid(PPT_SHOW.getClassID());

bos.reset();
poifs.writeFilesystem(bos);

return bos.toByteArray();
} catch(IOException e){
throw new RuntimeException(bla,e);
}
}

static byte [] getSampleXLS(){
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
sheet.createRow(5).createCell(2).setCellValue(你好,你喜欢embeddet objekts,所以我们把一个ole在你的ole,所以你可以保存一个文件,而你保存一个文件); bb = bte =
try {
wb.write(bos);

POIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
poifs.getRoot()。setStorageClsid(XLS_WORKBOOK.getClassID());

bos.reset();
poifs.writeFilesystem(bos);

return bos.toByteArray();
} catch(IOException e){
throw new RuntimeException(bla,e);
}
}

static byte [] getSampleTXT(){
返回所有你的基地都属于我们.getBytes();
}

/ **
*要定义,如何创建一个预览图片作为开始,我已经只花了
*一个虚拟图像,其中将被替换,当用户激活ole
*对象
*
*不是一个替代品:
* http://stackoverflow.com/questions/16704624/how-
* to-print-a-workbook-file-made-using-apache-poi-and-java
*
* @return预览图像的图像索引
* /
static int generatePreview(HSSFWorkbook工作簿,String mimetype){
try {
String url =;
if(application / powerpoint.equals(mimetype)){
url =http://upload.wikimedia.org/wikipedia/commons/thumb/a/a2/LibreOffice_Impress_icon_3.3.1_48_px。 SVG / 40像素-LibreOffice_Impress_icon_3.3.1_48_px.svg.png;
} else if(application / excel.equals(mimetype)){
url =http://upload.wikimedia.org/wikipedia/commons/thumb/2/2d/LibreOffice_Calc_icon_3.3.1 _48_px.svg / 40像素-LibreOffice_Calc_icon_3.3.1_48_px.svg.png;
} else if(text / plain.equals(mimetype)){
url =http://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/LibreOffice_Writer_icon_3.3.1 _48_px.svg / 40像素-LibreOffice_Writer_icon_3.3.1_48_px.svg.png;
}

InputStream is = new URL(url).openStream();
byte previewImg [] = IOUtils.toByteArray(is);
is.close();
int pictIdx = workbook.addPicture(previewImg,HSSFWorkbook.PICTURE_TYPE_PNG);
return pictIdx;
} catch(IOException e){
throw new RuntimeException(not really?,e);
}
}

/ *
*助手 - 确定零终止字符串(ASCIIZ)的长度。
* /
private static int getStringLength(byte [] data,int ofs){
int len = 0;
while(len + ofs< data.length& data [ofs + len]!= 0){
len ++;
}
len ++;
return len;
}

}

适应的Ole10Native类的POI与写支持:

  import java.io. *; 
import org.apache.poi.poifs.filesystem。*;
import org.apache.poi.util。*;

/ **
*表示一个Ole10Native记录,它被包含在OLE2文档中嵌入的某些二进制文件
*。
*
* @author Rainer Schwarze
* /
public class Ole10Native2 {
public static final String OLE10_NATIVE =\\\Ole10Native;
protected static final String ISO1 =ISO-8859-1;

//(在原始记录中出现的字段:)
protected int totalSize; // 4个字节,记录总数不包括
//字段
protected short flags1 = 2; // 2 bytes,unknown,mostly [02 00]
protected String label; // ASCIIZ,存储在此字段中没有
//终止零
protected String fileName; // ASCIIZ,存储在此字段中没有
//终止零
protected short flags2 = 0; // 2 bytes,unknown,mostly [00 00]
protected short unknown1 = 3;
protected String命令; // ASCIIZ,存储在此字段中没有
//终止零
protected byte [] dataBuffer; //变化的大小,实际的本机数据
protected short flags3 = 0; //一些最后的标志?或零终结器?
//有时不在


/ **
*从嵌入式OLE对象创建此类的实例。 OLE
* Object预计将包含一个流&{01} Ole10Native&其中
*包含与此类相关的实际数据。
*
* @param poifs
* POI文件系统对象
* @return返回此类的一个实例
* @throws IOException
*在IO错误
* @throws Ole10NativeException
*无效或未被接受的数据格式
* /
public static Ole10Native2 createFromEmbeddedOleObject(POIFSFileSystem poifs)throws IOException,Ole10NativeException {
return createFromEmbeddedOleObject(poifs .getRoot());
}

/ **
*从嵌入式OLE对象创建此类的实例。 OLE
* Object预计将包含一个流&{01} Ole10Native&其中
*包含与此类相关的实际数据。
*
* @param目录
* POI文件系统对象
* @return返回此类的一个实例
* @throws IOException
*在IO错误
* @throws Ole10NativeException
*无效或未被接受的数据格式
* /
public static Ole10Native2 createFromEmbeddedOleObject(DirectoryNode directory)throws IOException,Ole10NativeException {
boolean plain = false ;

尝试{
directory.getEntry(\\\Ole10ItemName);
plain = true;
} catch(FileNotFoundException ex){
plain = false;
}

DocumentEntry nativeEntry =(DocumentEntry)directory.getEntry(OLE10_NATIVE);
byte [] data = new byte [nativeEntry.getSize()];
directory.createDocumentInputStream(nativeEntry).read(data);

返回新的Ole10Native2(data,0,plain);
}

/ **
*创建一个实例,并根据给定的
*缓冲区中的数据填充字段。
*
* @param data
*包含Ole10Native记录的缓冲区
* @param offset
*缓冲区中记录的起始偏移量
* @throws Ole10NativeException
*无效或未被接受的数据格式
* /
public Ole10Native2(byte [] data,int offset)throws Ole10NativeException {
this(data,offset,false );
}

/ **
*创建一个实例,并根据给定的
*缓冲区中的数据填充字段。
*
* @param data
*包含Ole10Native记录的缓冲区
* @param offset
*缓冲区中记录的起始偏移量
* @param plain
*指定的'plain'格式,无文件名
* @throws Ole10NativeException
*无效或未被接受的数据格式
* /
public Ole10Native2(byte [ ] data,int offset,boolean plain)throws Ole10NativeException {
int ofs = offset; //当前偏移量,初始化为

if(data.length< offset + 2){
throw new Ole10NativeException(data is too small);
}

totalSize = LittleEndian.getInt(data,ofs);
ofs + = LittleEndianConsts.INT_SIZE;

if(plain){
dataBuffer = new byte [totalSize - 4];
System.arraycopy(data,4,dataBuffer,0,dataBuffer.length);
int dataSize = totalSize - 4;

byte [] oleLabel = new byte [8];
System.arraycopy(dataBuffer,0,oleLabel,0,Math.min(dataBuffer.length,8));
label =ole-+ HexDump.toHex(oleLabel);
fileName = label;
command = label;
} else {
flags1 = LittleEndian.getShort(data,ofs);
ofs + = LittleEndianConsts.SHORT_SIZE;

int len = getStringLength(data,ofs);
label = StringUtil.getFromCompressedUnicode(data,ofs,len - 1);
ofs + = len;

len = getStringLength(data,ofs);
fileName = StringUtil.getFromCompressedUnicode(data,ofs,len - 1);
ofs + = len;

flags2 = LittleEndian.getShort(data,ofs);
ofs + = LittleEndianConsts.SHORT_SIZE;

unknown1 = LittleEndian.getShort(data,ofs);
ofs + = LittleEndianConsts.SHORT_SIZE;

len = LittleEndian.getInt(data,ofs);
ofs + = LittleEndianConsts.INT_SIZE;

command = StringUtil.getFromCompressedUnicode(data,ofs,len - 1);
ofs + = len;

if(totalSize< ofs){
throw new Ole10NativeException(Invalid Ole10Native);
}

int dataSize = LittleEndian.getInt(data,ofs);
ofs + = LittleEndianConsts.INT_SIZE;

if(dataSize< 0 || totalSize - (ofs - LittleEndianConsts.INT_SIZE)< dataSize){
throw new Ole10NativeException(Invalid Ole10Native);
}

dataBuffer = new byte [dataSize];
System.arraycopy(data,ofs,dataBuffer,0,dataSize);
ofs + = dataSize;

// if(unknown1.length> 0){
// flags3 = LittleEndian.getShort(data,ofs);
// ofs + = LittleEndianConsts.SHORT_SIZE;
//} else {
// flags3 = 0;
//}
}
}

public Ole10Native2(){}

/ *
*助手 - 确定长度零终止字符串(ASCIIZ)。
* /
private static int getStringLength(byte [] data,int ofs){
int len = 0;
while(len + ofs< data.length& data [ofs + len]!= 0){
len ++;
}
len ++;
return len;
}

/ **
*返回totalSize字段的值 -
*结构的总长度为totalSize + 4(此字段的值+该字段的大小)。
*
* @返回totalSize
* /
public int getTotalSize(){
return totalSize;
}

/ **
*返回flags1 - 当前未知 - 通常为0x0002。
*
* @返回flags1
* /
public short getFlags1(){
return flags1;
}

/ **
*返回标签字段 - 通常是文件的名称(不含
*目录),但可能是
*打包/嵌入数据。
*
* @返回标签
* /
public String getLabel(){
return label;
}

/ **
*返回fileName字段 - 通常是要嵌入的文件的名称
*,包括完整路径。
*
* @返回fileName
* /
public String getFileName(){
return fileName;
}

/ **
*返回flags2 - 当前未知 - 大部分为0x0000。
*
* @返回flags2
* /
public short getFlags2(){
return flags2;
}

/ **
*返回未知1字段 - 当前未知。
*
* @返回未知1
* /
public short getUnknown1(){
return unknown1;
}

/ **
*返回unknown2字段 - 当前是一个字节[3] - 主要是{0,0,
* 0}。
*
* @返回未知2
* /
// public short getUnknown2(){
// return unknown2;
//}

/ **
*返回命令字段 - 通常要嵌入的文件的名称
*包括完整路径,可能是一个命令在嵌入
*文件时指定。
*
* @返回命令
* /
public String getCommand(){
return command;
}

/ **
*返回嵌入文件的大小。如果大小为0(零),则不嵌入数据
*。为确保没有嵌入任何数据,请检查
*是否{@link #getDataBuffer()}返回< code> null< / code> ;.
*
* @返回dataSize
* /
public int getDataSize(){
return dataBuffer.length;
}

/ **
*返回包含嵌入文件数据的缓冲区,或
*< code> null< / code>如果没有嵌入数据。请注意,嵌入可以
*提供有关数据的信息,但不包括实际数据。
*(所以标签,文件名等可用,但此方法返回
*< code> null< / code> ;.)
*
* @返回dataBuffer
* /
public byte [] getDataBuffer(){
return dataBuffer;
}

/ **
*返回flags3 - 当前未知。
*
* @返回flags3
* /
public short getFlags3(){
return flags3;
}

/ **
*将内容打印机输出为OutputStream,在将
*文件写回磁盘时使用(通常,原子类将保持它们的字节
*,但是非原子类只会从
*子元素中请求字节,然后将它们放在头上并返回)
* /
public void writeOut OutputStream out)throws IOException {
byte intbuf [] = new byte [LittleEndianConsts.INT_SIZE];
byte shortbuf [] = new byte [LittleEndianConsts.SHORT_SIZE];
byte bytebuf [] = new byte [LittleEndianConsts.BYTE_SIZE];
// LittleEndian.putInt(_header,4,_data.length); bb = bte =
bos.write(intbuf); //总大小,稍后会确定。

LittleEndian.putShort(shortbuf,0,getFlags1());
bos.write(shortbuf);

bos.write(getLabel()。getBytes(ISO1));
bos.write(0);

bos.write(getFileName()。getBytes(ISO1));
bos.write(0);

LittleEndian.putShort(shortbuf,0,getFlags2());
bos.write(shortbuf);

LittleEndian.putShort(shortbuf,0,getUnknown1());
bos.write(shortbuf);

LittleEndian.putInt(intbuf, 0, getCommand().length()+1);
bos.write(intbuf);

bos.write(getCommand().getBytes(ISO1));
bos.write(0);

LittleEndian.putInt(intbuf, 0, getDataBuffer().length);
bos.write(intbuf);

bos.write(getDataBuffer());

LittleEndian.putShort(shortbuf, 0, getFlags3());
bos.write(shortbuf);

// update total size - length of length-field (4 bytes)
byte data[] = bos.toByteArray();
totalSize = data.length - LittleEndianConsts.INT_SIZE;
LittleEndian.putInt(data, 0, totalSize);

out.write(data);
}

public void setFlags1(short flags1) {
this.flags1 = flags1;
}

public void setFlags2(short flags2) {
this.flags2 = flags2;
}

public void setFlags3(short flags3) {
this.flags3 = flags3;
}

public void setLabel(String label) {
this.label = label;
}

public void setFileName(String fileName) {
this.fileName = fileName;
}

public void setCommand(String command) {
this.command = command;
}

public void setUnknown1(short unknown1) {
this.unknown1 = unknown1;
}

// public void setUnknown2(short unknown2) {
// this.unknown2 = unknown2;
// }

public void setDataBuffer(byte dataBuffer[]) {
this.dataBuffer = dataBuffer;
}
}


I am exporting data to Excel file using Apache POI. In one weird requirement, I need to embed one file in the Excel using this POI. I have the file, and can be taken into streams or as byte arrays. After googling for much time, I am in a doubt whether POI really supports my requirement. Can we embed files into Excel? :-(

Cheers, Anoop

解决方案

Ok, this took very long to finally work out, as there were a few things which didn't look very important at the beginning, but actually corrupted the file when they haven't been set right - especially in the Ole10Native wrapper, part of the unknown2 field actually contained the size (in bytes) of the following command string.

But first things first:

When you want to embed arbitrary files into one of the office formats, your best bet is to use the OLE 1.0 packager. It will be typically used, when you select insert->object from file.

So I've re-engineered an Excel 2003 file containing a PPT. As mentioned in my comment above, Excel will store its embedded objects in DirectoryNodes named "MBD....". In case of a Ole 1.0 Packager object, the interesting data will be found in the \1Ole10Native entry.

When you've inserted the data, you'll need to link it somehow in the Excel sheet. This is done by an EscherObject similar to a picture entry with additional attached records.

Apart from the many undocumented flags, there were a few things which puzzled me:

  • are the storage ids for the embedded objects just randomly assigned or is there some kind of number system?
  • I've searched for a more detailed description of the Ole10Native wrapper and especially for the ole 1.0 packager format, but apart of the M$ docu which sketchily handles it as one big byte chunk, most sources did some reengineering which looked very similar to the poi Ole10Native class ... of course the idea to check the libre office source came also to mind, but I have to admit the ones I've checked, only confused me :(
  • which one is the right clsid for the embedded object? ... i.e. for powerpoint there are quite a few. So if in doubt, obviously you'll need to lookup the clsid by a previously saved file from Office
  • Excel 2010 generates Biff8 files which embedded objects can't be opened by Libre Office!?!
  • the ole10Native object contains among other things a command line entry. would be interesting if someone can start other things than the embedded object with it ...
  • the BiffViewer crashed when I've used preview images bigger than some chunk size (~6kb). So either images would need to be chunked or the BiffViewer implementation is wrong ... this also caused some confusing for a while ...

Tested with POI 3.9, Libre Office 4.0, Office 2010 (I don't have Office 2003 anymore ...)

import java.awt.Color;
import java.io.*;
import java.lang.reflect.*;
import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.filechooser.FileSystemView;

import org.apache.poi.ddf.*;
import org.apache.poi.hpsf.ClassID;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.model.*;
import org.apache.poi.hslf.model.ShapeTypes;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hssf.dev.BiffViewer;
import org.apache.poi.hssf.model.*;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.*;

@SuppressWarnings("unused")
public class PoiOlePptInXls {

    public static final OleType PACKAGE = new OleType("{0003000C-0000-0000-C000-000000000046}");
    public static final OleType PPT_SHOW = new OleType("{64818D10-4F9B-11CF-86EA-00AA00B929E8}");
    public static final OleType XLS_WORKBOOK = new OleType("{00020841-0000-0000-C000-000000000046}");
    public static final OleType TXT_ONLY = new OleType("{5e941d80-bf96-11cd-b579-08002b30bfeb}"); // ???

    static class OleType {
        final String classId;
        OleType(String classId) {
            this.classId = classId;
        }
        ClassID getClassID() {
            ClassID cls = new ClassID();
            byte clsBytes[] = cls.getBytes();
            String clsStr = classId.replaceAll("[{}-]", "");
            for (int i=0; i<clsStr.length(); i+=2) {
                clsBytes[i/2] = (byte)Integer.parseInt(clsStr.substring(i, i+2), 16);
            }
            return cls;
        }
    }   

    public static void main(String[] args) throws Exception {
        POIFSFileSystem poifs = new POIFSFileSystem(); 

        HSSFWorkbook wb = new HSSFWorkbook();
        HSSFSheet sheet = wb.createSheet();
        HSSFPatriarch patriarch = sheet.createDrawingPatriarch();

        int previewIdxPpt = generatePreview(wb, "application/powerpoint");
        int storageIdPpt = packageOleData(poifs, getSamplePPT(), "Example.ppt", "Example.ppt", "Example.ppt");
        int previewIdxXls = generatePreview(wb, "application/excel");
        int storageIdXls = packageOleData(poifs, getSampleXLS(), "Example.xls", "Example.xls", "Example.xls");
        int previewIdxTxt = generatePreview(wb, "text/plain");
        int storageIdTxt = packageOleData(poifs, getSampleTXT(), "Example.txt", "Example.txt", "Example.txt");

        int rowoffset = 5;
        int coloffset = 5;

        CreationHelper ch = wb.getCreationHelper();
        HSSFClientAnchor anchor = (HSSFClientAnchor)ch.createClientAnchor();
        anchor.setAnchor((short)(2+coloffset), 1+rowoffset, 0, 0, (short)(3+coloffset), 5+rowoffset, 0, 0);
        anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

        HSSFObjectData oleShape = createObjectData(poifs, storageIdPpt, 1, anchor, previewIdxPpt);
        addShape(patriarch, oleShape);

        anchor = (HSSFClientAnchor)ch.createClientAnchor();
        anchor.setAnchor((short)(5+coloffset), 1+rowoffset, 0, 0, (short)(6+coloffset), 5+rowoffset, 0, 0);
        anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

        oleShape = createObjectData(poifs, storageIdXls, 2, anchor, previewIdxXls);
        addShape(patriarch, oleShape);

        anchor = (HSSFClientAnchor)ch.createClientAnchor();
        anchor.setAnchor((short)(3+coloffset), 10+rowoffset, 0, 0, (short)(5+coloffset), 11+rowoffset, 0, 0);
        anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

        oleShape = createObjectData(poifs, storageIdTxt, 3, anchor, previewIdxTxt);
        addShape(patriarch, oleShape);

        anchor = (HSSFClientAnchor)ch.createClientAnchor();
        anchor.setAnchor((short)(1+coloffset), -2+rowoffset, 0, 0, (short)(7+coloffset), 14+rowoffset, 0, 0);
        anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

        HSSFSimpleShape circle = patriarch.createSimpleShape(anchor);
        circle.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
        circle.setNoFill(true);

        poifs.getRoot().createDocument("Workbook", new ByteArrayInputStream(wb.getBytes()));

        FileOutputStream fos = new FileOutputStream("ole_ppt_in_xls.xls");
        poifs.writeFilesystem(fos);
        fos.close();
    }

    static void addShape(HSSFPatriarch patriarch, HSSFShape shape) throws Exception {
        patriarch.addShape(shape);
        Method m = HSSFPatriarch.class.getDeclaredMethod("onCreate", HSSFShape.class);
        m.setAccessible(true);
        m.invoke(patriarch, shape);
    }

    static HSSFObjectData createObjectData(POIFSFileSystem poifs, int storageId, int objectIdx, HSSFClientAnchor anchor, int previewIdx) {
        ObjRecord obj = new ObjRecord();
        CommonObjectDataSubRecord ftCmo = new CommonObjectDataSubRecord();
        ftCmo.setObjectType(CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE);
        ftCmo.setObjectId(objectIdx);
        ftCmo.setLocked(true);
        ftCmo.setPrintable(true);
        ftCmo.setAutofill(true);
        ftCmo.setAutoline(true);
        ftCmo.setReserved1(0);
        ftCmo.setReserved2(0);
        ftCmo.setReserved3(0);
        obj.addSubRecord(ftCmo);

        obj.addSubRecord(SubRecord.createSubRecord(new LittleEndianByteArrayInputStream(new byte[]{7,0,2,0,2,0}), 0));
        obj.addSubRecord(SubRecord.createSubRecord(new LittleEndianByteArrayInputStream(new byte[]{8,0,2,0,1,0}), 0));

        EmbeddedObjectRefSubRecord ftPictFmla;
        try {
            Constructor<EmbeddedObjectRefSubRecord> con = EmbeddedObjectRefSubRecord.class.getDeclaredConstructor();
            con.setAccessible(true);
            ftPictFmla = con.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("oops", e);
        }

        setField(ftPictFmla, "field_2_unknownFormulaData", new byte[]{2, 0, 0, 0, 0});
        setField(ftPictFmla, "field_4_ole_classname", "Paket");
        setField(ftPictFmla, "field_5_stream_id", (Integer)storageId);

        obj.addSubRecord(ftPictFmla);
        obj.addSubRecord(new EndSubRecord());

        // create temporary picture, but don't attach it.
        // It's neccessary to create the sp-container, which need to be minimal modified
        // for oleshapes

        HSSFPicture shape = new HSSFPicture(null, anchor);
        EscherContainerRecord spContainer;

        try {
            Method m = HSSFPicture.class.getDeclaredMethod("createSpContainer");
            m.setAccessible(true);
            spContainer = (EscherContainerRecord)m.invoke(shape);
        } catch (Exception e) {
            throw new RuntimeException("oops", e);
        }

        EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
        spRecord.setFlags(spRecord.getFlags() |  EscherSpRecord.FLAG_OLESHAPE);
        spRecord.setShapeType((byte)0x4B);
        EscherOptRecord optRecord = spContainer.getChildById(EscherOptRecord.RECORD_ID);

        EscherProperty ep = new EscherSimpleProperty(EscherProperties.BLIP__PICTUREID, false, false, 1);
        optRecord.addEscherProperty(ep);

        DirectoryEntry oleRoot;
        try {
            oleRoot = (DirectoryEntry)poifs.getRoot().getEntry(formatStorageId(storageId));
        } catch (FileNotFoundException e) {
            throw new RuntimeException("oops", e);
        }
        HSSFObjectData oleShape = new HSSFObjectData(spContainer, obj, oleRoot); 
        oleShape.setPictureIndex(previewIdx);
        return oleShape;
    }

    static void setField(Object clazz, String fieldname, Object value) {
        try {
            Field f = clazz.getClass().getDeclaredField(fieldname);
            f.setAccessible(true);
            f.set(clazz, value);
        } catch (Exception e) {
            throw new RuntimeException("oops", e);
        }
    }

    static void addOleStreamEntry(DirectoryEntry dir) throws IOException {
        final String OLESTREAM_NAME = "\u0001Ole";
        if (!dir.hasEntry(OLESTREAM_NAME)) {
            // the following data was taken from an example libre office document
            // beside this "\u0001Ole" record there were several other records, e.g. CompObj,
            // OlePresXXX, but it seems, that they aren't neccessary
            byte oleBytes[] = { 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
            dir.createDocument(OLESTREAM_NAME, new ByteArrayInputStream(oleBytes));
        }       
    }

    static String formatStorageId(int storageId) {
        return String.format("MBD%1$08X", storageId);
    }

    static int packageOleData(POIFSFileSystem poifs, byte oleData[], String label, String fileName, String command) throws IOException {
        DirectoryNode root = poifs.getRoot();
        // get free MBD-Node
        int storageId = 0;
        DirectoryEntry oleDir = null;
        do {
            String storageStr = formatStorageId(++storageId);
            if (!root.hasEntry(storageStr)) {
                oleDir = root.createDirectory(storageStr);
                oleDir.setStorageClsid(PACKAGE.getClassID());
            }
        } while (oleDir == null);

        addOleStreamEntry(oleDir);

        Ole10Native2 oleNative = new Ole10Native2();
        oleNative.setLabel(label);
        oleNative.setFileName(fileName);
        oleNative.setCommand(command);
        oleNative.setDataBuffer(oleData);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        oleNative.writeOut(bos);
        byte buf1[] = bos.toByteArray();

        oleDir.createDocument(Ole10Native2.OLE10_NATIVE, new ByteArrayInputStream(buf1));

        return storageId;
    }

    static byte[] getSamplePPT() {
        HSLFSlideShow ss = HSLFSlideShow.create();
        SlideShow ppt = new SlideShow(ss);
        Slide slide = ppt.createSlide();

        AutoShape sh1 = new AutoShape(ShapeTypes.Star32);
        sh1.setAnchor(new java.awt.Rectangle(50, 50, 100, 200));
        sh1.setFillColor(Color.red);
        slide.addShape(sh1);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ppt.write(bos);

            POIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray())); 
            poifs.getRoot().setStorageClsid(PPT_SHOW.getClassID());

            bos.reset();
            poifs.writeFilesystem(bos);

            return bos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("bla", e);
        }
    }

    static byte[] getSampleXLS() {
        HSSFWorkbook wb = new HSSFWorkbook();
        HSSFSheet sheet = wb.createSheet();
        sheet.createRow(5).createCell(2).setCellValue("yo dawg i herd you like embeddet objekts, so we put a ole in your ole so you can save a file while you save a file");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            wb.write(bos);

            POIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray())); 
            poifs.getRoot().setStorageClsid(XLS_WORKBOOK.getClassID());

            bos.reset();
            poifs.writeFilesystem(bos);

            return bos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("bla", e);
        }
    }

    static byte[] getSampleTXT() {
        return "All your base are belong to us".getBytes();
    }

    /**
     * to be defined, how to create a preview image for a start, I've taken just
     * a dummy image, which will be replaced, when the user activates the ole
     * object
     * 
     * not really an alternativ:
     * http://stackoverflow.com/questions/16704624/how-
     * to-print-a-workbook-file-made-using-apache-poi-and-java
     * 
     * @return image index of the preview image
     */
    static int generatePreview(HSSFWorkbook workbook, String mimetype) {
        try {
            String url = "";
            if ("application/powerpoint".equals(mimetype)) {
                url = "http://upload.wikimedia.org/wikipedia/commons/thumb/a/a2/LibreOffice_Impress_icon_3.3.1_48_px.svg/40px-LibreOffice_Impress_icon_3.3.1_48_px.svg.png";
            } else if ("application/excel".equals(mimetype)) {
                url = "http://upload.wikimedia.org/wikipedia/commons/thumb/2/2d/LibreOffice_Calc_icon_3.3.1_48_px.svg/40px-LibreOffice_Calc_icon_3.3.1_48_px.svg.png";
            } else if ("text/plain".equals(mimetype)) {
                url = "http://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/LibreOffice_Writer_icon_3.3.1_48_px.svg/40px-LibreOffice_Writer_icon_3.3.1_48_px.svg.png";
            }

            InputStream is = new URL(url).openStream();
            byte previewImg[] = IOUtils.toByteArray(is);
            is.close();
            int pictIdx = workbook.addPicture(previewImg, HSSFWorkbook.PICTURE_TYPE_PNG);
            return pictIdx;
        } catch (IOException e) {
            throw new RuntimeException("not really?", e);
        }
    }

    /*
     * Helper - determine length of zero terminated string (ASCIIZ).
     */
    private static int getStringLength(byte[] data, int ofs) {
        int len = 0;
        while (len + ofs < data.length && data[ofs + len] != 0) {
            len++;
        }
        len++;
        return len;
    }

}

The adapted Ole10Native class of POI with write-support:

import java.io.*;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.util.*;

/**
 * Represents an Ole10Native record which is wrapped around certain binary files
 * being embedded in OLE2 documents.
 * 
 * @author Rainer Schwarze
 */
public class Ole10Native2 {
    public static final String OLE10_NATIVE = "\u0001Ole10Native";
    protected static final String ISO1 = "ISO-8859-1";

    // (the fields as they appear in the raw record:)
    protected int totalSize; // 4 bytes, total size of record not including this
                                // field
    protected short flags1 = 2; // 2 bytes, unknown, mostly [02 00]
    protected String label; // ASCIIZ, stored in this field without the
                            // terminating zero
    protected String fileName; // ASCIIZ, stored in this field without the
                                // terminating zero
    protected short flags2 = 0; // 2 bytes, unknown, mostly [00 00]
    protected short unknown1 = 3; 
    protected String command; // ASCIIZ, stored in this field without the
                                // terminating zero
    protected byte[] dataBuffer; // varying size, the actual native data
    protected short flags3 = 0; // some final flags? or zero terminators?,
                                // sometimes not there


    /**
     * Creates an instance of this class from an embedded OLE Object. The OLE
     * Object is expected to include a stream &quot;{01}Ole10Native&quot; which
     * contains the actual data relevant for this class.
     * 
     * @param poifs
     *            POI Filesystem object
     * @return Returns an instance of this class
     * @throws IOException
     *             on IO error
     * @throws Ole10NativeException
     *             on invalid or unexcepted data format
     */
    public static Ole10Native2 createFromEmbeddedOleObject(POIFSFileSystem poifs) throws IOException, Ole10NativeException {
        return createFromEmbeddedOleObject(poifs.getRoot());
    }

    /**
     * Creates an instance of this class from an embedded OLE Object. The OLE
     * Object is expected to include a stream &quot;{01}Ole10Native&quot; which
     * contains the actual data relevant for this class.
     * 
     * @param directory
     *            POI Filesystem object
     * @return Returns an instance of this class
     * @throws IOException
     *             on IO error
     * @throws Ole10NativeException
     *             on invalid or unexcepted data format
     */
    public static Ole10Native2 createFromEmbeddedOleObject(DirectoryNode directory) throws IOException, Ole10NativeException {
        boolean plain = false;

        try {
            directory.getEntry("\u0001Ole10ItemName");
            plain = true;
        } catch (FileNotFoundException ex) {
            plain = false;
        }

        DocumentEntry nativeEntry = (DocumentEntry) directory.getEntry(OLE10_NATIVE);
        byte[] data = new byte[nativeEntry.getSize()];
        directory.createDocumentInputStream(nativeEntry).read(data);

        return new Ole10Native2(data, 0, plain);
    }

    /**
     * Creates an instance and fills the fields based on the data in the given
     * buffer.
     * 
     * @param data
     *            The buffer containing the Ole10Native record
     * @param offset
     *            The start offset of the record in the buffer
     * @throws Ole10NativeException
     *             on invalid or unexcepted data format
     */
    public Ole10Native2(byte[] data, int offset) throws Ole10NativeException {
        this(data, offset, false);
    }

    /**
     * Creates an instance and fills the fields based on the data in the given
     * buffer.
     * 
     * @param data
     *            The buffer containing the Ole10Native record
     * @param offset
     *            The start offset of the record in the buffer
     * @param plain
     *            Specified 'plain' format without filename
     * @throws Ole10NativeException
     *             on invalid or unexcepted data format
     */
    public Ole10Native2(byte[] data, int offset, boolean plain) throws Ole10NativeException {
        int ofs = offset; // current offset, initialized to start

        if (data.length < offset + 2) {
            throw new Ole10NativeException("data is too small");
        }

        totalSize = LittleEndian.getInt(data, ofs);
        ofs += LittleEndianConsts.INT_SIZE;

        if (plain) {
            dataBuffer = new byte[totalSize - 4];
            System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length);
            int dataSize = totalSize - 4;

            byte[] oleLabel = new byte[8];
            System.arraycopy(dataBuffer, 0, oleLabel, 0, Math.min(dataBuffer.length, 8));
            label = "ole-" + HexDump.toHex(oleLabel);
            fileName = label;
            command = label;
        } else {
            flags1 = LittleEndian.getShort(data, ofs);
            ofs += LittleEndianConsts.SHORT_SIZE;

            int len = getStringLength(data, ofs);
            label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
            ofs += len;

            len = getStringLength(data, ofs);
            fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
            ofs += len;

            flags2 = LittleEndian.getShort(data, ofs);
            ofs += LittleEndianConsts.SHORT_SIZE;

            unknown1 = LittleEndian.getShort(data, ofs);
            ofs += LittleEndianConsts.SHORT_SIZE;

            len = LittleEndian.getInt(data, ofs);
            ofs += LittleEndianConsts.INT_SIZE;

            command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
            ofs += len;

            if (totalSize < ofs) {
                throw new Ole10NativeException("Invalid Ole10Native");
            }

            int dataSize = LittleEndian.getInt(data, ofs);
            ofs += LittleEndianConsts.INT_SIZE;

            if (dataSize < 0 || totalSize - (ofs - LittleEndianConsts.INT_SIZE) < dataSize) {
                throw new Ole10NativeException("Invalid Ole10Native");
            }

            dataBuffer = new byte[dataSize];
            System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
            ofs += dataSize;

//          if (unknown1.length > 0) {
//              flags3 = LittleEndian.getShort(data, ofs);
//              ofs += LittleEndianConsts.SHORT_SIZE;
//          } else {
//              flags3 = 0;
//          }
        }
    }

    public Ole10Native2() {}

    /*
     * Helper - determine length of zero terminated string (ASCIIZ).
     */
    private static int getStringLength(byte[] data, int ofs) {
        int len = 0;
        while (len + ofs < data.length && data[ofs + len] != 0) {
            len++;
        }
        len++;
        return len;
    }

    /**
     * Returns the value of the totalSize field - the total length of the
     * structure is totalSize + 4 (value of this field + size of this field).
     * 
     * @return the totalSize
     */
    public int getTotalSize() {
        return totalSize;
    }

    /**
     * Returns flags1 - currently unknown - usually 0x0002.
     * 
     * @return the flags1
     */
    public short getFlags1() {
        return flags1;
    }

    /**
     * Returns the label field - usually the name of the file (without
     * directory) but probably may be any name specified during
     * packaging/embedding the data.
     * 
     * @return the label
     */
    public String getLabel() {
        return label;
    }

    /**
     * Returns the fileName field - usually the name of the file being embedded
     * including the full path.
     * 
     * @return the fileName
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * Returns flags2 - currently unknown - mostly 0x0000.
     * 
     * @return the flags2
     */
    public short getFlags2() {
        return flags2;
    }

    /**
     * Returns unknown1 field - currently unknown.
     * 
     * @return the unknown1
     */
    public short getUnknown1() {
        return unknown1;
    }

    /**
     * Returns the unknown2 field - currently being a byte[3] - mostly {0, 0,
     * 0}.
     * 
     * @return the unknown2
     */
//  public short getUnknown2() {
//      return unknown2;
//  }

    /**
     * Returns the command field - usually the name of the file being embedded
     * including the full path, may be a command specified during embedding the
     * file.
     * 
     * @return the command
     */
    public String getCommand() {
        return command;
    }

    /**
     * Returns the size of the embedded file. If the size is 0 (zero), no data
     * has been embedded. To be sure, that no data has been embedded, check
     * whether {@link #getDataBuffer()} returns <code>null</code>.
     * 
     * @return the dataSize
     */
    public int getDataSize() {
        return dataBuffer.length;
    }

    /**
     * Returns the buffer containing the embedded file's data, or
     * <code>null</code> if no data was embedded. Note that an embedding may
     * provide information about the data, but the actual data is not included.
     * (So label, filename etc. are available, but this method returns
     * <code>null</code>.)
     * 
     * @return the dataBuffer
     */
    public byte[] getDataBuffer() {
        return dataBuffer;
    }

    /**
     * Returns the flags3 - currently unknown.
     * 
     * @return the flags3
     */
    public short getFlags3() {
        return flags3;
    }

    /**
     * Have the contents printer out into an OutputStream, used when writing a
     * file back out to disk (Normally, atom classes will keep their bytes
     * around, but non atom classes will just request the bytes from their
     * children, then chuck on their header and return)
     */
    public void writeOut(OutputStream out) throws IOException {
        byte intbuf[] = new byte[LittleEndianConsts.INT_SIZE];
        byte shortbuf[] = new byte[LittleEndianConsts.SHORT_SIZE];
        byte bytebuf[] = new byte[LittleEndianConsts.BYTE_SIZE];
        // LittleEndian.putInt(_header, 4, _data.length);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bos.write(intbuf); // total size, will be determined later ..

        LittleEndian.putShort(shortbuf, 0, getFlags1());
        bos.write(shortbuf);

        bos.write(getLabel().getBytes(ISO1));
        bos.write(0);

        bos.write(getFileName().getBytes(ISO1));
        bos.write(0);

        LittleEndian.putShort(shortbuf, 0, getFlags2());
        bos.write(shortbuf);

        LittleEndian.putShort(shortbuf, 0, getUnknown1());
        bos.write(shortbuf);

        LittleEndian.putInt(intbuf, 0, getCommand().length()+1);
        bos.write(intbuf);

        bos.write(getCommand().getBytes(ISO1));
        bos.write(0);

        LittleEndian.putInt(intbuf, 0, getDataBuffer().length);
        bos.write(intbuf);

        bos.write(getDataBuffer());

        LittleEndian.putShort(shortbuf, 0, getFlags3());
        bos.write(shortbuf);

        // update total size - length of length-field (4 bytes)
        byte data[] = bos.toByteArray();
        totalSize = data.length - LittleEndianConsts.INT_SIZE;
        LittleEndian.putInt(data, 0, totalSize);

        out.write(data);
    }

    public void setFlags1(short flags1) {
        this.flags1 = flags1;
    }

    public void setFlags2(short flags2) {
        this.flags2 = flags2;
    }

    public void setFlags3(short flags3) {
        this.flags3 = flags3;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public void setCommand(String command) {
        this.command = command;
    }

    public void setUnknown1(short unknown1) {
        this.unknown1 = unknown1;
    }

//  public void setUnknown2(short unknown2) {
//      this.unknown2 = unknown2;
//  }

    public void setDataBuffer(byte dataBuffer[]) {
        this.dataBuffer = dataBuffer;
    }
}

这篇关于使用Apache POI将文件嵌入到Excel中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆