Skip to content

Commit

Permalink
Create POI File System from File (#391)
Browse files Browse the repository at this point in the history
  • Loading branch information
joniles committed Nov 18, 2022
1 parent abd71ea commit 308e09c
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/changes/changes.xml
Expand Up @@ -7,6 +7,7 @@
<body>
<release date="unreleased" version="10.13.1">
<action dev="joniles" type="update">Handle missing default calendar when reading a PMXML file.</action>
<action dev="joniles" type="update">When reading an MPP file using a file name or `File` instance, ensure a more memory-efficient approach is used.</action>
</release>
<release date="2022-11-16" version="10.13.0">
<action dev="joniles" type="update">Add support for reading a resource assignment's cost account from P6 schedules.</action>
Expand Down
40 changes: 31 additions & 9 deletions src/main/java/net/sf/mpxj/mpp/MPPReader.java
Expand Up @@ -23,6 +23,8 @@

package net.sf.mpxj.mpp;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
Expand All @@ -35,6 +37,7 @@
import net.sf.mpxj.ProjectCalendar;
import net.sf.mpxj.Resource;
import net.sf.mpxj.TaskField;
import net.sf.mpxj.common.AutoCloseableHelper;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
Expand All @@ -58,19 +61,12 @@ public final class MPPReader extends AbstractProjectStreamReader
{
try
{
//
// Open the file system
//
POIFSFileSystem fs = new POIFSFileSystem(is);

return read(fs);

return read(new POIFSFileSystem(is));
}

catch (IOException ex)
{

throw new MPXJException(MPXJException.READ_ERROR, ex);

}
}

Expand All @@ -79,6 +75,32 @@ public final class MPPReader extends AbstractProjectStreamReader
return Collections.singletonList(read(inputStream));
}

@Override public ProjectFile read(File file) throws MPXJException
{
POIFSFileSystem fs = null;

try
{
// Note we provide this version of the read method rather than using
// the AbstractProjectStreamReader version as we can work with the File
// instance directly for reduced memory consumption and the ability
// to open larger MPP files.
fs = new POIFSFileSystem(file);
ProjectFile projectFile = read(fs);
return projectFile;
}

catch (IOException ex)
{
throw new MPXJException(MPXJException.READ_ERROR, ex);
}

finally
{
AutoCloseableHelper.closeQuietly(fs);
}
}

/**
* This method allows us to peek into the OLE compound document to extract the file format.
* This allows the UniversalProjectReader to determine if this is an MPP file, or if
Expand Down
59 changes: 52 additions & 7 deletions src/main/java/net/sf/mpxj/reader/UniversalProjectReader.java
Expand Up @@ -178,7 +178,30 @@ private List<ProjectFile> readInternal(File file) throws MPXJException
return readInternal(inputStream);
}

/**
* Internal implementation of the read method using an InputStream.
*
* @param inputStream input stream to read from
* @return list of schedules
*/
private List<ProjectFile> readInternal(InputStream inputStream) throws MPXJException
{
return readInternal(null, inputStream);
}

/**
* Internal implementation of the read method. This primarily works with the
* supplied InputStream instance however if the stream is from a file,
* we'll pass a File instance too. This allows us to read MPP files
* using a more memory-efficient approach based on the File than would
* otherwise be possible using an input stream. This also avoids a hard
* limit imposed by POI when reading certain very large files.
*
* @param file optional File instance
* @param inputStream input stream to read from
* @return list of schedules
*/
private List<ProjectFile> readInternal(File file, InputStream inputStream) throws MPXJException
{
try
{
Expand Down Expand Up @@ -226,7 +249,7 @@ private List<ProjectFile> readInternal(InputStream inputStream) throws MPXJExcep

if (matchesFingerprint(buffer, OLE_COMPOUND_DOC_FINGERPRINT))
{
return handleOleCompoundDocument(bis);
return handleOleCompoundDocument(file, bis);
}

if (matchesFingerprint(buffer, MSPDI_FINGERPRINT_1) || matchesFingerprint(buffer, MSPDI_FINGERPRINT_2) || matchesFingerprint(buffer, MSPDI_FINGERPRINT_3))
Expand Down Expand Up @@ -405,32 +428,54 @@ private List<ProjectFile> readProjectFile(ProjectReader reader, File file) throw
/**
* We have an OLE compound document... but is it an MPP file?
*
* @param file File object
* @param stream file input stream
* @return ProjectFile instance
*/
private List<ProjectFile> handleOleCompoundDocument(InputStream stream) throws Exception
private List<ProjectFile> handleOleCompoundDocument(File file, InputStream stream) throws Exception
{
POIFSFileSystem fs;
boolean closeFile = false;

try
{
fs = new POIFSFileSystem(new CloseIgnoringInputStream(stream));
if (file == null)
{
fs = new POIFSFileSystem(new CloseIgnoringInputStream(stream));
}
else
{
fs = new POIFSFileSystem(file);
closeFile = true;
}
}

catch (Exception ex)
{
return Collections.emptyList();
}

String fileFormat = MPPReader.getFileFormat(fs);
if (fileFormat != null && fileFormat.startsWith("MSProject"))
try
{
String fileFormat = MPPReader.getFileFormat(fs);
if (fileFormat == null || !fileFormat.startsWith("MSProject"))
{
return Collections.emptyList();
}

MPPReader reader = new MPPReader();
addListenersToReader(reader);
reader.setProperties(m_properties);
return Collections.singletonList(reader.read(fs));
}
return Collections.emptyList();

finally
{
if (closeFile)
{
AutoCloseableHelper.closeQuietly(fs);
}
}
}

/**
Expand Down Expand Up @@ -562,7 +607,7 @@ private List<ProjectFile> handleFile(File file) throws Exception
try
{
fis = new FileInputStream(file);
List<ProjectFile> projectFiles = readInternal(fis);
List<ProjectFile> projectFiles = readInternal(file, fis);
fis.close();
return projectFiles;
}
Expand Down

0 comments on commit 308e09c

Please sign in to comment.