commons-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stephen Huey <stephen.h...@gmail.com>
Subject Re: Windows XP Compressed Folders doesn't recognize my VFS-based zip file
Date Tue, 05 Jan 2010 22:27:54 GMT
For posterity's sake, I meant to come back here and post the solution a
while ago.  I've got the full story here in this blog post:

http://stephenhuey.wordpress.com/2010/01/01/docxgae/

The relatively new Microsoft Office formats ending with extensions such as
.docx and .xlsx are zip files of directories of text files (XML) and images.
 Tried-and-true Java libraries for writing the old binary Microsoft Word and
Excel formats aren't yet supported on Google App Engine, so I was glad to
get this working since it was a required feature for our application.

My modified code for the zipDir and addDir methods is below.  As you can
see, I'm closing the ZipOutputStream rather than the OutputStream:


+ + + + + + + + + + + + + + + + + + + +


import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.FileType;

public class FileObjectHelper {

 public static FileObject createFolder(FileSystemManager fsManager, String
absolutePath) throws FileSystemException {
 FileObject theFolder = fsManager.resolveFile( absolutePath );
 if ( theFolder.exists() == false) {
 theFolder.createFolder();
 }
 return theFolder;
 }

 public static FileObject createFile(FileSystemManager fsManager, String
absolutePath) throws FileSystemException {
 FileObject theFile = fsManager.resolveFile( absolutePath );
 if ( theFile.exists() == false) {
 theFile.createFile();
 }
 return theFile;
 }

 public static void zipDir(FileObject docxZipFile, FileObject
directoryToZip) throws IOException {
 OutputStream out = docxZipFile.getContent().getOutputStream();
 ZipOutputStream zout = new ZipOutputStream(out);
 addDir(directoryToZip, zout, "");
 zout.close(); // make sure you close the ZipOutputStream, not the
OutputStream!
 }

 public static void addDir(FileObject dirObj, ZipOutputStream zout, String
basePathSoFar) throws IOException {
 FileObject[] files = dirObj.getChildren();
 byte[] tmpBuf = new byte[1024];

 for (int i = 0; i < files.length; i++) {
 FileObject currentFile = files[i];
 String currentFileBaseName = currentFile.getName().getBaseName();

 if (currentFile.getType().equals(FileType.FOLDER)) {
 addDir(currentFile, zout, basePathSoFar + currentFileBaseName + "/");

 } else { // else it's a file, not a directory
 BufferedInputStream bis = new
BufferedInputStream(currentFile.getContent().getInputStream());
 zout.putNextEntry(new ZipEntry(basePathSoFar + currentFileBaseName));
 int len;
 while ((len = bis.read(tmpBuf)) != -1) {
 zout.write(tmpBuf, 0, len);
 }
 zout.closeEntry();
 bis.close();
 } // end if
 } // end for loop
 } // end addDir
}


+ + + + + + + + + + + + + + + + + + + +





Happy New Year,
Stephen Huey




On Tue, Dec 15, 2009 at 1:54 AM, Stephen Huey <stephen.huey@gmail.com>wrote:

> I'm using GaeVFS which is based on Commons VFS, and I feel that my question
> is probably more relevant to Commons VFS than the Google App Engine layer on
> top of it.  My goal is to construct valid zip files on Google App Engine,
> and I successfully used GaeVFS to create zip files that Mac OS X can open
> and that Winzip on Windows can open, but Windows XP Compressed Folders
> refused to acknowledge files in subdirectories in the zip until I removed
> the leading forward slash from a ZipEntry whose name began with a
> directory.
>
> For example, the following basic class successfully generates a zip file
> containing a file in a subdirectory that Windows XP Compressed Folders will
> open:
>
>
> import java.util.zip.*;
> import java.io.*;
> public class ZipThis {
>   public static void main(String args[]) throws IOException {
>     if (args.length < 1) {
>       System.err.println("usage: java ZipThis Zip.zip");
>       System.exit(-1);
>     }
>     File zipFile = new File(args[0]);
>     if (zipFile.exists()) {
>       System.err.println("Zip file already exists, please try another");
>       System.exit(-2);
>     }
>     FileOutputStream fos = new FileOutputStream(zipFile);
>     ZipOutputStream zos = new ZipOutputStream(fos);
>     int bytesRead;
>     String[] files = new String[] {"D:/stylessub/styles.xml",
> "D:/document.xml"};
>     String[] entryName = new String[] {"stylessub/styles.xml",
> "document.xml"};
>     byte[] buffer = new byte[1024];
>     for (int i=0, n=files.length; i < n; i++) {
>       String name = files[i];
>       File file = new File(name);
>       if (!file.exists()) {
>         System.out.println("Skipping: " + name);
>         continue;
>       }
>       BufferedInputStream bis = new BufferedInputStream(new
> FileInputStream(file));
>       ZipEntry entry = new ZipEntry(entryName[i]);
>       zos.putNextEntry(entry);
>       while ((bytesRead = bis.read(buffer)) != -1) {
>         zos.write(buffer, 0, bytesRead);
>       }
>       bis.close();
>       zos.closeEntry();
>
>     }
>     zos.close();
>   }
> }
>
>
> Now I'm trying to get my virtual file system code to generate a zip that
> Windows won't complain about with this error message:  "The Compressed
> (zipped) Folder is isvalid or corrupted."  I want to meet the Microsoft
> employee who wrote that error message!  :)
>
> So here's some initial setup in my servlet:
>
> FileObject docxZipFile = FileObjectHelper.createFile(fsManager,
> "gae://gaevfs/generatedZip/docxFile.zip");
> FileObject docxRootFolder = FileObjectHelper.createFolder(fsManager,
> "gae://gaevfs/docxDirectory");
>
> These helper methods are being used:
>
>     public static FileObject createFile(FileSystemManager fsManager, String
> absolutePath) throws FileSystemException {
>         FileObject theFile = fsManager.resolveFile( absolutePath );
>         if ( theFile.exists() == false) {
>             theFile.createFile();
>         }
>         return theFile;
>     }
>
>     public static FileObject createFolder(FileSystemManager fsManager,
> String absolutePath) throws FileSystemException {
>         FileObject theFolder = fsManager.resolveFile( absolutePath );
>         if ( theFolder.exists() == false) {
>             theFolder.createFolder();
>         }
>         return theFolder;
>     }
>
> Pardon the near redundancy.  Anyway, the next line begins the zipping:
>
> FileObjectHelper.zipDir(docxZipFile, docxRootFolder);
>
>
> Here's the zipDir method:
>
>
>     public static void zipDir(FileObject docxZipFile, FileObject
> directoryToZip) throws Exception {
>         OutputStream out = docxZipFile.getContent().getOutputStream();
>         //ByteArrayOutputStream bos = new ByteArrayOutputStream();
>         ZipOutputStream zout = new ZipOutputStream(out);
>         addDir(directoryToZip, zout, directoryToZip.getName().getBaseName()
> + "/");
>         out.close();
>         //return bos.toByteArray();
>     }
>
>
> As you can see, I played with pushing down a byte array of the resulting
> zip file on the servlet output stream instead of saving the generated zip
> file to the virtual file system, but that didn't make my error message go
> away.  Here's the addDir method:
>
>
>     public static void addDir(FileObject dirObj, ZipOutputStream out,
> String basePathSoFar) throws IOException {
>         System.out.println("addDir for " + dirObj.getName());
>         FileObject[] files = dirObj.getChildren();
>         byte[] tmpBuf = new byte[1024];
>
>         for (int i = 0; i < files.length; i++) {
>             FileObject currentFile = files[i];
>             System.out.println("currentFile is " +
> currentFile.getName().getBaseName());
>             if (currentFile.getType().equals(FileType.FOLDER)) {
>                 addDir(currentFile, out, basePathSoFar +
> currentFile.getName().getBaseName() + "/");
>
>             } else {
>
>                 System.out.println("making entry for: " + basePathSoFar +
> currentFile.getName().getBaseName());
>                 InputStream in = currentFile.getContent().getInputStream();
>                 out.putNextEntry(new ZipEntry(basePathSoFar +
> currentFile.getName().getBaseName()));
>                 int len;
>                 while ((len = in.read(tmpBuf)) != -1) {
>                     out.write(tmpBuf, 0, len);
>                 }
>                 out.closeEntry();
>                 in.close();
>             }
>         } // end for loop
>     }
>
>
> I used to have the buffer read check for greater than zero rather than not
> equal to negative one.  Anyway, for some reason this doesn't work even
> though it seems very close to the working program above.  I'll paste the
> print statements down below so you can see that it doesn't appear as if VFS
> is putting any extra junk into the ZipEntry names or anything like that.
>
> I'd love to try out any bright ideas anyone has!
>
> Thanks so much...
>
>
>
> The server is running at http://localhost:8888/
> Passing the zip file:
> /D:/workspace/my-app/war/gaevfs/generatedZip/docxFile.zip
> Writing this zip file: docxFile.zip
> Zipping up  directory: docxDirectory
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory
> currentFile is _rels
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/_rels
> currentFile is docProps
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/docProps
> currentFile is app.xml
> making entry for: docxDirectory/docProps/app.xml
> currentFile is core.xml
> making entry for: docxDirectory/docProps/core.xml
> currentFile is word
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word
> currentFile is document.xml
> making entry for: docxDirectory/word/document.xml
> currentFile is fontTable.xml
> making entry for: docxDirectory/word/fontTable.xml
> currentFile is settings.xml
> making entry for: docxDirectory/word/settings.xml
> currentFile is styles.xml
> making entry for: docxDirectory/word/styles.xml
> currentFile is webSettings.xml
> making entry for: docxDirectory/word/webSettings.xml
> currentFile is _rels
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word/_rels
> currentFile is document.xml.rels
> making entry for: docxDirectory/word/_rels/document.xml.rels
> currentFile is media
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word/media
> currentFile is image1.jpeg
> making entry for: docxDirectory/word/media/image1.jpeg
> currentFile is image2.jpeg
> making entry for: docxDirectory/word/media/image2.jpeg
> currentFile is theme
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word/theme
> currentFile is theme1.xml
> making entry for: docxDirectory/word/theme/theme1.xml
> currentFile is [Content_Types].xml
> making entry for: docxDirectory/[Content_Types].xml
> Passing the zip file:
> /D:/workspace/my-app/war/gaevfs/generatedZip/betterzip.zip
> Writing this zip file: betterzip.zip
> Zipping up  directory: docxDirectory
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory
> currentFile is _rels
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/_rels
> currentFile is docProps
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/docProps
> currentFile is app.xml
> making entry for: docxDirectory/docProps/app.xml
> currentFile is core.xml
> making entry for: docxDirectory/docProps/core.xml
> currentFile is word
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word
> currentFile is document.xml
> making entry for: docxDirectory/word/document.xml
> currentFile is fontTable.xml
> making entry for: docxDirectory/word/fontTable.xml
> currentFile is settings.xml
> making entry for: docxDirectory/word/settings.xml
> currentFile is styles.xml
> making entry for: docxDirectory/word/styles.xml
> currentFile is webSettings.xml
> making entry for: docxDirectory/word/webSettings.xml
> currentFile is _rels
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word/_rels
> currentFile is document.xml.rels
> making entry for: docxDirectory/word/_rels/document.xml.rels
> currentFile is media
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word/media
> currentFile is image1.jpeg
> making entry for: docxDirectory/word/media/image1.jpeg
> currentFile is image2.jpeg
> making entry for: docxDirectory/word/media/image2.jpeg
> currentFile is theme
> addDir for gae:///D:/workspace/my-app/war/gaevfs/docxDirectory/word/theme
> currentFile is theme1.xml
> making entry for: docxDirectory/word/theme/theme1.xml
> currentFile is [Content_Types].xml
> making entry for: docxDirectory/[Content_Types].xml
>
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message