Bug 56437 - [PATCH] Streaming write support in NPOIFS
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1588887 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
821d3ca5bd
commit
14a7ae6316
@ -412,7 +412,11 @@ public class DirectoryNode
|
|||||||
final POIFSWriterListener writer)
|
final POIFSWriterListener writer)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
return createDocument(new POIFSDocument(name, size, _path, writer));
|
if(_nfilesystem != null) {
|
||||||
|
return createDocument(new NPOIFSDocument(name, size, _nfilesystem, writer));
|
||||||
|
} else {
|
||||||
|
return createDocument(new POIFSDocument(name, size, _path, writer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -30,7 +31,6 @@ import org.apache.poi.poifs.common.POIFSConstants;
|
|||||||
import org.apache.poi.poifs.dev.POIFSViewable;
|
import org.apache.poi.poifs.dev.POIFSViewable;
|
||||||
import org.apache.poi.poifs.property.DocumentProperty;
|
import org.apache.poi.poifs.property.DocumentProperty;
|
||||||
import org.apache.poi.util.HexDump;
|
import org.apache.poi.util.HexDump;
|
||||||
import org.apache.poi.util.IOUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class manages a document in the NIO POIFS filesystem.
|
* This class manages a document in the NIO POIFS filesystem.
|
||||||
@ -72,21 +72,12 @@ public final class NPOIFSDocument implements POIFSViewable {
|
|||||||
{
|
{
|
||||||
this._filesystem = filesystem;
|
this._filesystem = filesystem;
|
||||||
|
|
||||||
// Buffer the contents into memory. This is a bit icky...
|
final int bigBlockSize = POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE;
|
||||||
// TODO Replace with a buffer up to the mini stream size, then streaming write
|
BufferedInputStream bis = new BufferedInputStream(stream, bigBlockSize+1);
|
||||||
byte[] contents;
|
bis.mark(bigBlockSize);
|
||||||
if(stream instanceof ByteArrayInputStream) {
|
|
||||||
ByteArrayInputStream bais = (ByteArrayInputStream)stream;
|
|
||||||
contents = new byte[bais.available()];
|
|
||||||
bais.read(contents);
|
|
||||||
} else {
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
IOUtils.copy(stream, baos);
|
|
||||||
contents = baos.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we need to store as a mini stream or a full one?
|
// Do we need to store as a mini stream or a full one?
|
||||||
if(contents.length <= POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) {
|
if(bis.skip(bigBlockSize) < bigBlockSize) {
|
||||||
_stream = new NPOIFSStream(filesystem.getMiniStore());
|
_stream = new NPOIFSStream(filesystem.getMiniStore());
|
||||||
_block_size = _filesystem.getMiniStore().getBlockStoreBlockSize();
|
_block_size = _filesystem.getMiniStore().getBlockStoreBlockSize();
|
||||||
} else {
|
} else {
|
||||||
@ -94,14 +85,49 @@ public final class NPOIFSDocument implements POIFSViewable {
|
|||||||
_block_size = _filesystem.getBlockStoreBlockSize();
|
_block_size = _filesystem.getBlockStoreBlockSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start from the beginning
|
||||||
|
bis.reset();
|
||||||
|
|
||||||
// Store it
|
// Store it
|
||||||
_stream.updateContents(contents);
|
OutputStream os = _stream.getOutputStream();
|
||||||
|
byte buf[] = new byte[1024];
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
for (int readBytes; (readBytes = bis.read(buf)) != -1; length += readBytes) {
|
||||||
|
os.write(buf, 0, readBytes);
|
||||||
|
}
|
||||||
|
|
||||||
// And build the property for it
|
// And build the property for it
|
||||||
this._property = new DocumentProperty(name, contents.length);
|
this._property = new DocumentProperty(name, length);
|
||||||
_property.setStartBlock(_stream.getStartBlock());
|
_property.setStartBlock(_stream.getStartBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NPOIFSDocument(String name, int size, NPOIFSFileSystem filesystem, POIFSWriterListener writer)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this._filesystem = filesystem;
|
||||||
|
|
||||||
|
if (size < POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) {
|
||||||
|
_stream = new NPOIFSStream(filesystem.getMiniStore());
|
||||||
|
_block_size = _filesystem.getMiniStore().getBlockStoreBlockSize();
|
||||||
|
} else {
|
||||||
|
_stream = new NPOIFSStream(filesystem);
|
||||||
|
_block_size = _filesystem.getBlockStoreBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream innerOs = _stream.getOutputStream();
|
||||||
|
DocumentOutputStream os = new DocumentOutputStream(innerOs, size);
|
||||||
|
POIFSDocumentPath path = new POIFSDocumentPath(name.split("\\\\"));
|
||||||
|
String docName = path.getComponent(path.length()-1);
|
||||||
|
POIFSWriterEvent event = new POIFSWriterEvent(os, path, docName, size);
|
||||||
|
writer.processPOIFSWriterEvent(event);
|
||||||
|
innerOs.close();
|
||||||
|
|
||||||
|
// And build the property for it
|
||||||
|
this._property = new DocumentProperty(name, size);
|
||||||
|
_property.setStartBlock(_stream.getStartBlock());
|
||||||
|
}
|
||||||
|
|
||||||
int getDocumentBlockSize() {
|
int getDocumentBlockSize() {
|
||||||
return _block_size;
|
return _block_size;
|
||||||
}
|
}
|
||||||
|
@ -117,12 +117,14 @@ public class NPOIFSFileSystem extends BlockStore
|
|||||||
// Mark us as having a single empty BAT at offset 0
|
// Mark us as having a single empty BAT at offset 0
|
||||||
_header.setBATCount(1);
|
_header.setBATCount(1);
|
||||||
_header.setBATArray(new int[] { 0 });
|
_header.setBATArray(new int[] { 0 });
|
||||||
_bat_blocks.add(BATBlock.createEmptyBATBlock(bigBlockSize, false));
|
BATBlock bb = BATBlock.createEmptyBATBlock(bigBlockSize, false);
|
||||||
|
bb.setOurBlockIndex(0);
|
||||||
|
_bat_blocks.add(bb);
|
||||||
|
|
||||||
setNextBlock(0, POIFSConstants.FAT_SECTOR_BLOCK);
|
setNextBlock(0, POIFSConstants.FAT_SECTOR_BLOCK);
|
||||||
|
|
||||||
// Now associate the properties with the empty block
|
|
||||||
_property_table.setStartBlock(1);
|
|
||||||
setNextBlock(1, POIFSConstants.END_OF_CHAIN);
|
setNextBlock(1, POIFSConstants.END_OF_CHAIN);
|
||||||
|
|
||||||
|
_property_table.setStartBlock(POIFSConstants.END_OF_CHAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -483,13 +485,11 @@ public class NPOIFSFileSystem extends BlockStore
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected int getFreeBlock() throws IOException {
|
protected int getFreeBlock() throws IOException {
|
||||||
|
int numSectors = bigBlockSize.getBATEntriesPerBlock();
|
||||||
|
|
||||||
// First up, do we have any spare ones?
|
// First up, do we have any spare ones?
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for(int i=0; i<_bat_blocks.size(); i++) {
|
for (BATBlock bat : _bat_blocks) {
|
||||||
int numSectors = bigBlockSize.getBATEntriesPerBlock();
|
|
||||||
|
|
||||||
// Check this one
|
|
||||||
BATBlock bat = _bat_blocks.get(i);
|
|
||||||
if(bat.hasFreeSectors()) {
|
if(bat.hasFreeSectors()) {
|
||||||
// Claim one of them and return it
|
// Claim one of them and return it
|
||||||
for(int j=0; j<numSectors; j++) {
|
for(int j=0; j<numSectors; j++) {
|
||||||
@ -699,8 +699,13 @@ public class NPOIFSFileSystem extends BlockStore
|
|||||||
* Has our in-memory objects write their state
|
* Has our in-memory objects write their state
|
||||||
* to their backing blocks
|
* to their backing blocks
|
||||||
*/
|
*/
|
||||||
private void syncWithDataSource() throws IOException
|
private void syncWithDataSource() throws IOException {
|
||||||
{
|
// Properties
|
||||||
|
NPOIFSStream propStream = new NPOIFSStream(this, _header.getPropertyStart());
|
||||||
|
_property_table.preWrite();
|
||||||
|
_property_table.write(propStream);
|
||||||
|
// _header.setPropertyStart has been updated on write ...
|
||||||
|
|
||||||
// HeaderBlock
|
// HeaderBlock
|
||||||
HeaderBlockWriter hbw = new HeaderBlockWriter(_header);
|
HeaderBlockWriter hbw = new HeaderBlockWriter(_header);
|
||||||
hbw.writeBlock( getBlockAt(-1) );
|
hbw.writeBlock( getBlockAt(-1) );
|
||||||
@ -713,11 +718,6 @@ public class NPOIFSFileSystem extends BlockStore
|
|||||||
|
|
||||||
// SBATs
|
// SBATs
|
||||||
_mini_store.syncWithDataSource();
|
_mini_store.syncWithDataSource();
|
||||||
|
|
||||||
// Properties
|
|
||||||
_property_table.write(
|
|
||||||
new NPOIFSStream(this, _header.getPropertyStart())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
{
|
{
|
||||||
private BlockStore blockStore;
|
private BlockStore blockStore;
|
||||||
private int startBlock;
|
private int startBlock;
|
||||||
|
private OutputStream outStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for an existing stream. It's up to you
|
* Constructor for an existing stream. It's up to you
|
||||||
@ -92,7 +94,7 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
}
|
}
|
||||||
return new StreamBlockByteBufferIterator(startBlock);
|
return new StreamBlockByteBufferIterator(startBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the contents of the stream to the new
|
* Updates the contents of the stream to the new
|
||||||
* set of bytes.
|
* set of bytes.
|
||||||
@ -100,62 +102,16 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
* need to update the size in the property yourself
|
* need to update the size in the property yourself
|
||||||
*/
|
*/
|
||||||
public void updateContents(byte[] contents) throws IOException {
|
public void updateContents(byte[] contents) throws IOException {
|
||||||
// How many blocks are we going to need?
|
OutputStream os = getOutputStream();
|
||||||
int blockSize = blockStore.getBlockStoreBlockSize();
|
os.write(contents);
|
||||||
int blocks = (int)Math.ceil( ((double)contents.length) / blockSize );
|
os.close();
|
||||||
|
}
|
||||||
// Make sure we don't encounter a loop whilst overwriting
|
|
||||||
// the existing blocks
|
public OutputStream getOutputStream() throws IOException {
|
||||||
ChainLoopDetector loopDetector = blockStore.getChainLoopDetector();
|
if (outStream == null) {
|
||||||
|
outStream = new StreamBlockByteBuffer();
|
||||||
// Start writing
|
}
|
||||||
int prevBlock = POIFSConstants.END_OF_CHAIN;
|
return outStream;
|
||||||
int nextBlock = startBlock;
|
|
||||||
for(int i=0; i<blocks; i++) {
|
|
||||||
int thisBlock = nextBlock;
|
|
||||||
|
|
||||||
// Allocate a block if needed, otherwise figure
|
|
||||||
// out what the next block will be
|
|
||||||
if(thisBlock == POIFSConstants.END_OF_CHAIN) {
|
|
||||||
thisBlock = blockStore.getFreeBlock();
|
|
||||||
loopDetector.claim(thisBlock);
|
|
||||||
|
|
||||||
// We're on the end of the chain
|
|
||||||
nextBlock = POIFSConstants.END_OF_CHAIN;
|
|
||||||
|
|
||||||
// Mark the previous block as carrying on to us if needed
|
|
||||||
if(prevBlock != POIFSConstants.END_OF_CHAIN) {
|
|
||||||
blockStore.setNextBlock(prevBlock, thisBlock);
|
|
||||||
}
|
|
||||||
blockStore.setNextBlock(thisBlock, POIFSConstants.END_OF_CHAIN);
|
|
||||||
|
|
||||||
// If we've just written the first block on a
|
|
||||||
// new stream, save the start block offset
|
|
||||||
if(this.startBlock == POIFSConstants.END_OF_CHAIN) {
|
|
||||||
this.startBlock = thisBlock;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loopDetector.claim(thisBlock);
|
|
||||||
nextBlock = blockStore.getNextBlock(thisBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write it
|
|
||||||
ByteBuffer buffer = blockStore.createBlockIfNeeded(thisBlock);
|
|
||||||
int startAt = i*blockSize;
|
|
||||||
int endAt = Math.min(contents.length - startAt, blockSize);
|
|
||||||
buffer.put(contents, startAt, endAt);
|
|
||||||
|
|
||||||
// Update pointers
|
|
||||||
prevBlock = thisBlock;
|
|
||||||
}
|
|
||||||
int lastBlock = prevBlock;
|
|
||||||
|
|
||||||
// If we're overwriting, free any remaining blocks
|
|
||||||
NPOIFSStream toFree = new NPOIFSStream(blockStore, nextBlock);
|
|
||||||
toFree.free(loopDetector);
|
|
||||||
|
|
||||||
// Mark the end of the stream
|
|
||||||
blockStore.setNextBlock(lastBlock, POIFSConstants.END_OF_CHAIN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Streaming write support
|
// TODO Streaming write support
|
||||||
@ -222,5 +178,87 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class StreamBlockByteBuffer extends OutputStream {
|
||||||
|
byte oneByte[] = new byte[1];
|
||||||
|
ByteBuffer buffer;
|
||||||
|
// Make sure we don't encounter a loop whilst overwriting
|
||||||
|
// the existing blocks
|
||||||
|
ChainLoopDetector loopDetector;
|
||||||
|
int prevBlock, nextBlock;
|
||||||
|
|
||||||
|
protected StreamBlockByteBuffer() throws IOException {
|
||||||
|
loopDetector = blockStore.getChainLoopDetector();
|
||||||
|
prevBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
nextBlock = startBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createBlockIfNeeded() throws IOException {
|
||||||
|
if (buffer != null && buffer.hasRemaining()) return;
|
||||||
|
|
||||||
|
int thisBlock = nextBlock;
|
||||||
|
|
||||||
|
// Allocate a block if needed, otherwise figure
|
||||||
|
// out what the next block will be
|
||||||
|
if(thisBlock == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
thisBlock = blockStore.getFreeBlock();
|
||||||
|
loopDetector.claim(thisBlock);
|
||||||
|
|
||||||
|
// We're on the end of the chain
|
||||||
|
nextBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
|
||||||
|
// Mark the previous block as carrying on to us if needed
|
||||||
|
if(prevBlock != POIFSConstants.END_OF_CHAIN) {
|
||||||
|
blockStore.setNextBlock(prevBlock, thisBlock);
|
||||||
|
}
|
||||||
|
blockStore.setNextBlock(thisBlock, POIFSConstants.END_OF_CHAIN);
|
||||||
|
|
||||||
|
// If we've just written the first block on a
|
||||||
|
// new stream, save the start block offset
|
||||||
|
if(startBlock == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
startBlock = thisBlock;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loopDetector.claim(thisBlock);
|
||||||
|
nextBlock = blockStore.getNextBlock(thisBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = blockStore.createBlockIfNeeded(thisBlock);
|
||||||
|
|
||||||
|
// Update pointers
|
||||||
|
prevBlock = thisBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
oneByte[0] = (byte)(b & 0xFF);
|
||||||
|
write(oneByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||||
|
((off + len) > b.length) || ((off + len) < 0)) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
} else if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
createBlockIfNeeded();
|
||||||
|
int writeBytes = Math.min(buffer.remaining(), len);
|
||||||
|
buffer.put(b, off, writeBytes);
|
||||||
|
off += writeBytes;
|
||||||
|
len -= writeBytes;
|
||||||
|
} while (len > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
// If we're overwriting, free any remaining blocks
|
||||||
|
NPOIFSStream toFree = new NPOIFSStream(blockStore, nextBlock);
|
||||||
|
toFree.free(loopDetector);
|
||||||
|
|
||||||
|
// Mark the end of the stream
|
||||||
|
blockStore.setNextBlock(prevBlock, POIFSConstants.END_OF_CHAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.property;
|
package org.apache.poi.poifs.property;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -123,18 +123,35 @@ public final class NPropertyTable extends PropertyTableBase {
|
|||||||
return (int)Math.ceil(size / _bigBigBlockSize.getBigBlockSize());
|
return (int)Math.ceil(size / _bigBigBlockSize.getBigBlockSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare to be written
|
||||||
|
*/
|
||||||
|
public void preWrite() {
|
||||||
|
List<Property> pList = new ArrayList<Property>();
|
||||||
|
// give each property its index
|
||||||
|
int i=0;
|
||||||
|
for (Property p : _properties) {
|
||||||
|
// only handle non-null properties
|
||||||
|
if (p == null) continue;
|
||||||
|
p.setIndex(i++);
|
||||||
|
pList.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare each property for writing
|
||||||
|
for (Property p : pList) p.preWrite();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the properties out into the given low-level stream
|
* Writes the properties out into the given low-level stream
|
||||||
*/
|
*/
|
||||||
public void write(NPOIFSStream stream) throws IOException {
|
public void write(NPOIFSStream stream) throws IOException {
|
||||||
// TODO - Use a streaming write
|
OutputStream os = stream.getOutputStream();
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
for(Property property : _properties) {
|
for(Property property : _properties) {
|
||||||
if(property != null) {
|
if(property != null) {
|
||||||
property.writeData(baos);
|
property.writeData(os);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream.updateContents(baos.toByteArray());
|
os.close();
|
||||||
|
|
||||||
// Update the start position if needed
|
// Update the start position if needed
|
||||||
if(getStartBlock() != stream.getStartBlock()) {
|
if(getStartBlock() != stream.getStartBlock()) {
|
||||||
|
@ -17,13 +17,18 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import static org.hamcrest.core.IsEqual.equalTo;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import org.apache.poi.POIDataSamples;
|
import org.apache.poi.POIDataSamples;
|
||||||
import org.apache.poi.hpsf.PropertySet;
|
import org.apache.poi.hpsf.PropertySet;
|
||||||
import org.apache.poi.hpsf.PropertySetFactory;
|
import org.apache.poi.hpsf.PropertySetFactory;
|
||||||
@ -33,14 +38,17 @@ import org.apache.poi.poifs.property.NPropertyTable;
|
|||||||
import org.apache.poi.poifs.property.Property;
|
import org.apache.poi.poifs.property.Property;
|
||||||
import org.apache.poi.poifs.property.RootProperty;
|
import org.apache.poi.poifs.property.RootProperty;
|
||||||
import org.apache.poi.poifs.storage.HeaderBlock;
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the new NIO POIFSFileSystem implementation
|
* Tests for the new NIO POIFSFileSystem implementation
|
||||||
*/
|
*/
|
||||||
public final class TestNPOIFSFileSystem extends TestCase {
|
public final class TestNPOIFSFileSystem {
|
||||||
private static final POIDataSamples _inst = POIDataSamples.getPOIFSInstance();
|
private static final POIDataSamples _inst = POIDataSamples.getPOIFSInstance();
|
||||||
|
|
||||||
public void testBasicOpen() throws Exception {
|
@Test
|
||||||
|
public void basicOpen() throws Exception {
|
||||||
NPOIFSFileSystem fsA, fsB;
|
NPOIFSFileSystem fsA, fsB;
|
||||||
|
|
||||||
// With a simple 512 block file
|
// With a simple 512 block file
|
||||||
@ -58,7 +66,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropertiesAndFatOnRead() throws Exception {
|
@Test
|
||||||
|
public void propertiesAndFatOnRead() throws Exception {
|
||||||
NPOIFSFileSystem fsA, fsB;
|
NPOIFSFileSystem fsA, fsB;
|
||||||
|
|
||||||
// With a simple 512 block file
|
// With a simple 512 block file
|
||||||
@ -196,7 +205,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Check that for a given block, we can correctly figure
|
* Check that for a given block, we can correctly figure
|
||||||
* out what the next one is
|
* out what the next one is
|
||||||
*/
|
*/
|
||||||
public void testNextBlock() throws Exception {
|
@Test
|
||||||
|
public void nextBlock() throws Exception {
|
||||||
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
||||||
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
||||||
@ -253,7 +263,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
/**
|
/**
|
||||||
* Check we get the right data back for each block
|
* Check we get the right data back for each block
|
||||||
*/
|
*/
|
||||||
public void testGetBlock() throws Exception {
|
@Test
|
||||||
|
public void getBlock() throws Exception {
|
||||||
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
||||||
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
||||||
@ -322,7 +333,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Ask for free blocks where there are some already
|
* Ask for free blocks where there are some already
|
||||||
* to be had from the FAT
|
* to be had from the FAT
|
||||||
*/
|
*/
|
||||||
public void testGetFreeBlockWithSpare() throws Exception {
|
@Test
|
||||||
|
public void getFreeBlockWithSpare() throws Exception {
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
// Our first BAT block has spares
|
// Our first BAT block has spares
|
||||||
@ -349,7 +361,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Ask for free blocks where no free ones exist, and so the
|
* Ask for free blocks where no free ones exist, and so the
|
||||||
* file needs to be extended and another BAT/XBAT added
|
* file needs to be extended and another BAT/XBAT added
|
||||||
*/
|
*/
|
||||||
public void testGetFreeBlockWithNoneSpare() throws Exception {
|
@Test
|
||||||
|
public void getFreeBlockWithNoneSpare() throws Exception {
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
||||||
int free;
|
int free;
|
||||||
|
|
||||||
@ -479,7 +492,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Test that we can correctly get the list of directory
|
* Test that we can correctly get the list of directory
|
||||||
* entries, and the details on the files in them
|
* entries, and the details on the files in them
|
||||||
*/
|
*/
|
||||||
public void testListEntries() throws Exception {
|
@Test
|
||||||
|
public void listEntries() throws Exception {
|
||||||
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
||||||
NPOIFSFileSystem fsC = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi"));
|
NPOIFSFileSystem fsC = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi"));
|
||||||
@ -519,7 +533,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Tests that we can get the correct contents for
|
* Tests that we can get the correct contents for
|
||||||
* a document in the filesystem
|
* a document in the filesystem
|
||||||
*/
|
*/
|
||||||
public void testGetDocumentEntry() throws Exception {
|
@Test
|
||||||
|
public void getDocumentEntry() throws Exception {
|
||||||
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
||||||
NPOIFSFileSystem fsC = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi"));
|
NPOIFSFileSystem fsC = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi"));
|
||||||
@ -552,7 +567,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Read a file, write it and read it again.
|
* Read a file, write it and read it again.
|
||||||
* Then, alter+add some streams, write and read
|
* Then, alter+add some streams, write and read
|
||||||
*/
|
*/
|
||||||
public void testReadWriteRead() throws Exception {
|
@Test
|
||||||
|
public void readWriteRead() throws Exception {
|
||||||
// TODO
|
// TODO
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
@ -561,7 +577,8 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Create a new file, write it and read it again
|
* Create a new file, write it and read it again
|
||||||
* Then, add some streams, write and read
|
* Then, add some streams, write and read
|
||||||
*/
|
*/
|
||||||
public void testCreateWriteRead() throws Exception {
|
@Test
|
||||||
|
public void createWriteRead() throws Exception {
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem();
|
NPOIFSFileSystem fs = new NPOIFSFileSystem();
|
||||||
|
|
||||||
// Initially has a BAT but not SBAT
|
// Initially has a BAT but not SBAT
|
||||||
@ -577,10 +594,11 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
fs.writeFilesystem(baos);
|
fs.writeFilesystem(baos);
|
||||||
fs = new NPOIFSFileSystem(new ByteArrayInputStream(baos.toByteArray()));
|
fs = new NPOIFSFileSystem(new ByteArrayInputStream(baos.toByteArray()));
|
||||||
|
|
||||||
// Check it's still like that
|
// Property table entries have been added to the blocks
|
||||||
assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(0));
|
assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(0));
|
||||||
assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(1));
|
assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(1));
|
||||||
assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(2));
|
assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(2));
|
||||||
|
assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(3));
|
||||||
assertEquals(POIFSConstants.END_OF_CHAIN, fs.getRoot().getProperty().getStartBlock());
|
assertEquals(POIFSConstants.END_OF_CHAIN, fs.getRoot().getProperty().getStartBlock());
|
||||||
|
|
||||||
// Now add a normal stream and a mini stream
|
// Now add a normal stream and a mini stream
|
||||||
@ -588,6 +606,54 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
|
|
||||||
// TODO The rest of the test
|
// TODO The rest of the test
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writPOIFSWriterListener() throws Exception {
|
||||||
|
File testFile = POIDataSamples.getSpreadSheetInstance().getFile("Simple.xls");
|
||||||
|
NPOIFSFileSystem src = new NPOIFSFileSystem(testFile);
|
||||||
|
byte wbDataExp[] = IOUtils.toByteArray(src.createDocumentInputStream("Workbook"));
|
||||||
|
|
||||||
|
NPOIFSFileSystem nfs = new NPOIFSFileSystem();
|
||||||
|
copy(src.getRoot(), nfs.getRoot());
|
||||||
|
src.close();
|
||||||
|
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
nfs.writeFilesystem(bos);
|
||||||
|
nfs.close();
|
||||||
|
|
||||||
|
POIFSFileSystem pfs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
|
||||||
|
byte wbDataAct[] = IOUtils.toByteArray(pfs.createDocumentInputStream("Workbook"));
|
||||||
|
|
||||||
|
assertThat(wbDataExp, equalTo(wbDataAct));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copy(final DirectoryNode src, final DirectoryNode dest) throws IOException {
|
||||||
|
Iterator<Entry> srcIter = src.getEntries();
|
||||||
|
while(srcIter.hasNext()) {
|
||||||
|
Entry entry = srcIter.next();
|
||||||
|
if (entry.isDirectoryEntry()) {
|
||||||
|
DirectoryNode srcDir = (DirectoryNode)entry;
|
||||||
|
DirectoryNode destDir = (DirectoryNode)dest.createDirectory(srcDir.getName());
|
||||||
|
destDir.setStorageClsid(src.getStorageClsid());
|
||||||
|
copy(srcDir, destDir);
|
||||||
|
} else {
|
||||||
|
final DocumentNode srcDoc = (DocumentNode)entry;
|
||||||
|
// dest.createDocument(srcDoc.getName(), src.createDocumentInputStream(srcDoc));
|
||||||
|
dest.createDocument(srcDoc.getName(), srcDoc.getSize(), new POIFSWriterListener() {
|
||||||
|
public void processPOIFSWriterEvent(POIFSWriterEvent event) {
|
||||||
|
try {
|
||||||
|
DocumentInputStream dis = src.createDocumentInputStream(srcDoc);
|
||||||
|
IOUtils.copy(dis, event.getStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO Directory/Document write tests
|
// TODO Directory/Document write tests
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user