13 Replies Latest reply: Jun 7, 2011 2:33 AM by 867080 RSS

    Thumbnails Thumbs.db

    843799
      Hello,

      Does anybody know how to create an use the Microsoft "thumbs.db" file using java code?

      Thanks.
        • 1. Re: Thumbnails Thumbs.db
          843799
          It is created when thumbnail caching is enabled in Windows XP. What do you mean by "use it"? Load the images? No idea - but you could create your own thumbnails (they are just scaled down copies of images) and load them directly (as many websites do).
          • 2. Re: Thumbnails Thumbs.db
            843799
            I am sorry if I was not so clear.

            I say "use it" in the way a thumbnail database can be used. That is, I think, load the thumbnails already created by the system, or by a program, add new thumbnails and delete them, as an application might require.

            I know how to create my own thumbnails, but I am interested in the way this specific thumbs database is formatted, and how can I create my own thumbs.db file.

            Thank you.
            • 3. Re: Thumbnails Thumbs.db
              843799
              I know this thread is cold but I've found some information.

              Thumbs.db is in Compound File Binary File format. I've found documentation about it at http://longhorn.msdn.microsoft.com/lhsdk/docservices/overviews/binaryformat.aspx. The file reads like an old FAT filesystem, with multiple sectors. I was able to extract files which appear to be JPEG but are missing the quantization and huffman tables. I'm trying to use the imageio libraries to read these files but without any success so far.

              Has anyone had any success reading and viewing these thumbnails?

              Thanks, ...Mike
              • 4. Re: Thumbnails Thumbs.db
                843799
                Did you get anywhere with this? I'm approaching the same challenge now...

                cheers
                dim
                • 5. Re: Thumbnails Thumbs.db
                  843799
                  Heyho,

                  After a little research, I found the thumbs.db file is actually in Microsoft's OLE 2 Compound Document format. It's the same format that MS Office uses. After writing a lot of code to handle this format, a coworker pointed me to http://jakarta.apache.org/poi/index.html which will do all the breaking out of the file for you. I haven't ported my code over to that but it should work.

                  Once you get the JPEG files, you'll need to decode them. I wasn't able to get the imageio classes to handle them but the classes in com.sun.image.codec.jpeg were somewhat able to handle the images. Assuming the variable inStream is an input stream with the jpeg file, the following code will get you a BufferedImage:
                  JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inStream);
                  JPEGDecodeParam param = JPEGCodec.getDefaultJPEGEncodeParam(4, JPEGDecodeParam.COLOR_ID_RGBA);
                  decoder.setJPEGDecodeParam(param);
                  
                  BufferedImage originalBufferedImage = decoder.decodeAsBufferedImage();
                  At this point, the real hack bit of code started. The images colors were wrong. Green was first, followed by blue, and then red. (The alpha value was always zero) I ended up making a new BufferedImage and then copying over the image pixel by pixel, setting the colors properly.

                  In the end, I was able to handle this file properly. Hopefully this will help. I'm sure there's a way to use imageio instead of the com.sun classes but I wasn't able to figure it out. If you get try that and get it to work, I'd love to see the code.

                  Good Luck,
                  ...Mike
                  • 6. Re: Thumbnails Thumbs.db
                    843799
                    I'd be very interested in sharing code in this area, as I need to create a fake thumbs.db for some video files on a Linux server.

                    I particular have you worked out the filenames in the compound document? POI seems to give them as just numbers (01, 8 41 etc)
                    • 7. Re: Thumbnails Thumbs.db
                      843799
                      Mike,

                      Have you had another go using POI? The input stream I'm getting isn't recognised as a JPEG... .I'm using the following code:
                          InputStream stream = new FileInputStream("C:\\stuff\\pics2\\Thumbs.db");
                          POIFSFileSystem fs = new POIFSFileSystem(stream);
                      
                          DirectoryEntry root = fs.getRoot();
                          Entry entry = root.getEntry("3");
                          DocumentInputStream is = fs.createDocumentInputStream(entry.getName());
                          JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(is);
                          JPEGDecodeParam param = JPEGCodec.getDefaultJPEGEncodeParam(4, JPEGDecodeParam.COLOR_ID_RGBA);
                          decoder.setJPEGDecodeParam(param);
                          BufferedImage originalBufferedImage = decoder.decodeAsBufferedImage();
                      Where are you getting your inputstream from? btw - the above gives:
                      com.sun.image.codec.jpeg.ImageFormatException: Not a JPEG file: starts with 0x0c 0x00
                           at sun.awt.image.codec.JPEGImageDecoderImpl.readJPEGStream(Native Method)
                           at sun.awt.image.codec.JPEGImageDecoderImpl.decodeAsBufferedImage(JPEGImageDecoderImpl.java:210)
                           at com.colebatch.scratch.poi.ThumbsDBTest.main(ThumbsDBTest.java:36)
                           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                           at java.lang.reflect.Method.invoke(Method.java:324)
                           at com.intellij.rt.execution.application.AppMain.main(Unknown Source)
                      cheers
                      dim
                      • 8. Re: Thumbnails Thumbs.db
                        843799
                        Hello,

                        Once I programmed a perl script that read the names and number of files (this information is at the CATALOG block of the file) in thumbs.db and created dumb template files in a chosen dir (using any template image). Then you make Windows create thumbs.db of this dir. And so the perl script copies the jpegs codes from the original thumbs.db to the newly created, without changing the document structure (it means the same number of entries and filenames). When you open that dir again Windows "thinks" theres no need to refresh and show you the thumbs you wanna see instead of the template thumbs.
                        In this way i could see the thumbnails without having the original files, but i could have individual jpegs for each of them....
                        In that time POI didnt exist yet, now it appear to be easier to extract individual jpegs from thumbs.db.

                        I remember that the entries in the files were not the entire jpeg itself. You have to cut down the first bytes of the stream before using it in the decoder. Get some JPEG specification to see what is the first bytes of it so you will know where to cut.

                        However, i dont know if it will really work. The JPEGs in these files lack some information that seems to be provided by the Windows API at the time the thumnails are created. Please tell me if you have success.

                        • 9. Re: Thumbnails Thumbs.db
                          843799
                          I tried the following code:
                                          DocumentInputStream dStream = fs.createDocumentInputStream(nameEntry);
                                          dStream.skip(28);
                                          JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(dStream);    
                                          JPEGDecodeParam param = JPEGCodec.getDefaultJPEGEncodeParam(4,  JPEGDecodeParam.COLOR_ID_RGBA);    
                                          decoder.setJPEGDecodeParam(param);    
                                          BufferedImage originalBufferedImage = decoder.decodeAsBufferedImage();
                                          
                                          File aFile = new File("test.jpg");
                                          ImageIO.write(originalBufferedImage, "jpg", aFile);
                          When I skip the first 28 bytes, I cut down the header, so decode works ok. However the image generated is all black :(. Maybe decoding is not right?
                          • 10. Re: Thumbnails Thumbs.db
                            843799
                            I am able to export and display all Thumbnails from Thumbs.db now, but i can't find out their names.
                            The Thumnails itself are called 1,2,3,4... etc in the file. there is another file called "Catalog", when i look into the bytestream i can see all the filenames, but i dont understand the format of the data :/
                            in a very small, fresh thumbs.db, the twelveth byte left to the filename seems to show the files number, but it doesn't work on a larger, older (fragmented?) file.

                            Can anyone give me a tip?
                            • 11. Re: Thumbnails Thumbs.db
                              843799
                              Hello, have you find how to extract the Thumbnails filenames. I have the same problem. If you have the solution, can you help me ?
                              • 12. Re: Thumbnails Thumbs.db
                                843799
                                Any update about this? I would like to read and write to thumbs.db, will that possible?
                                • 13. Re: Thumbnails Thumbs.db
                                  867080
                                  there is a way to decode images whith their original names.

                                  Hope it can be usefull:

                                  /**
                                   * This software is free. Use at your own risk.
                                   */
                                  
                                  import java.awt.image.BufferedImage;
                                  import java.io.File;
                                  import java.io.FileInputStream;
                                  import java.io.IOException;
                                  import java.io.InputStream;
                                  import java.util.ArrayList;
                                  
                                  import javax.imageio.ImageIO;
                                  
                                  import org.apache.poi.poifs.filesystem.DirectoryNode;
                                  import org.apache.poi.poifs.filesystem.DocumentInputStream;
                                  import org.apache.poi.poifs.filesystem.POIFSFileSystem;
                                  import org.apache.poi.poifs.property.DocumentProperty;
                                  
                                  import com.sun.image.codec.jpeg.JPEGCodec;
                                  import com.sun.image.codec.jpeg.JPEGDecodeParam;
                                  import com.sun.image.codec.jpeg.JPEGImageDecoder;
                                  
                                  public class ThumbsReader {
                                       POIFSFileSystem fs;
                                  
                                       int counter = 0;
                                  
                                       String outputDir = ".";
                                  
                                       boolean DEBUG = false;
                                  
                                       public ThumbsReader(InputStream stream, String outDir) throws IOException {
                                            fs = new POIFSFileSystem(stream);
                                            outputDir = outDir;
                                       }
                                  
                                       public String space(int n) {
                                            StringBuffer s = new StringBuffer();
                                            for (int i = 0; i < n; i++)
                                                 s.append(' ');
                                            return s.toString();
                                       }
                                  
                                       public void handleProperty(DocumentProperty prop) {
                                            System.out.println("name=" + prop.getName());
                                            System.out.println("desc=" + prop.getShortDescription());
                                            System.out.println("startblock=" + prop.getStartBlock());
                                            System.out.println("storageclass=" + prop.getStorageClsid());
                                       }
                                  
                                       public void showDir(int indent, DirectoryNode d) {
                                            if (DEBUG)
                                                 System.out.println(d.getName());
                                            
                                            ArrayList<CatalogItem> CatalogItems = new ArrayList<CatalogItem>();
                                            
                                            DocumentInputStream is = null;
                                            try{
                                            is = fs.createDocumentInputStream("Catalog");
                                            
                                             short nNum1 = is.readShort();
                                           short nNum2 = is.readShort();
                                           int nThumbCount = is.readInt();
                                           int nThumbWidth = is.readInt();
                                           int nThumbHeight = is.readInt();
                                  
                                           for (int nIndex = 0; nIndex < nThumbCount; nIndex++)
                                           {
                                               CatalogItem item = new CatalogItem();
                                               item.nItemSize = is.readInt();
                                               item.nItemID = is.readInt();
                                               item.nNum3 = is.readShort();
                                               item.nNum4 = is.readShort();
                                               item.nNum5 = is.readShort();
                                               item.nNum6 = is.readShort();
                                               int usChar;
                                               while((usChar = is.readUShort()) != 0x0000)
                                               {
                                                   byte[] byChar = new byte[2];
                                                   byChar[0] =  (byte)(usChar & 0x00FF);;
                                                   byChar[1] = (byte)((usChar & 0xFF00) >> 8);
                                                   item.strFileName +=  byChar[1]==0?(char)byChar[0]:new String( byChar);
                                               }
                                               item.nNum7 = is.readShort();
                                               CatalogItems.add(item);
                                           }
                                            
                                            
                                            }catch (Exception e) {
                                                 e.printStackTrace();
                                            }          
                                  
                                            for (CatalogItem item: CatalogItems){
                                                 try {
                                                      String correctFileName = buildReverseString(item.nItemID);
                                  
                                                      is = fs.createDocumentInputStream(correctFileName);
                                                      int header_len = is.read();
                                                      for (int i = 1; i < header_len; i++) {
                                                           is.read();
                                                      }
                                                      
                                                      JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(is);
                                                      JPEGDecodeParam param = JPEGCodec
                                                                .getDefaultJPEGEncodeParam(4,
                                                                          JPEGDecodeParam.COLOR_ID_RGBA);
                                                      decoder.setJPEGDecodeParam(param);
                                                      BufferedImage originalBufferedImage = decoder
                                                                .decodeAsBufferedImage();
                                  
                                                      ImageIO.write(originalBufferedImage, "jpg", new File(
                                                                outputDir + File.separator + item.strFileName.replaceAll(".pdf", ".jpg")));
                                                      counter++;
                                  
                                                 } catch (Exception e) {
                                                      e.printStackTrace();
                                                 }
                                            }
                                       }
                                  
                                       private String buildReverseString(int nItemID)
                                      {
                                          String strItem = Integer.toString(nItemID);
                                          String strReverse = "";
                                          for (int nIndex = strItem.length() - 1; nIndex >= 0; nIndex--)
                                              strReverse += strItem.charAt(nIndex);
                                  
                                          return strReverse;
                                      }
                                       public void showFS(POIFSFileSystem fs) {
                                            DirectoryNode d = (DirectoryNode) fs.getRoot(); // TODO: get rid of cast
                                            showDir(0, d);
                                       }
                                  
                                       public void makeJPG() throws IOException {
                                            showFS(fs);
                                       }
                                  
                                       /**
                                        * @param args
                                        */
                                       public static void main(String[] args) {
                                  
                                            String thumbsFile = "Thumbs.db";
                                            String outDir = ".";
                                  
                                            if (args.length > 0) {
                                                 thumbsFile = args[0];
                                            }
                                            if (args.length > 1) {
                                                 outDir = args[1];
                                            }
                                            try {
                                                 InputStream stream = new FileInputStream(thumbsFile);
                                                 ThumbsReader r = new ThumbsReader(stream, outDir);
                                                 r.makeJPG();
                                            } catch (IOException e) {
                                                 e.printStackTrace();
                                            }
                                  
                                       }
                                  
                                  }
                                  
                                  class CatalogItem
                                  {
                                      public int nItemSize;
                                      public int nItemID;
                                      public short nNum3;
                                      public short nNum4;
                                      public short nNum5;
                                      public short nNum6;
                                      public String strFileName="";
                                      public short nNum7;
                                      
                                      @Override
                                      public String toString() {
                                           return nItemID + " - " + strFileName;
                                      }
                                  }
                                  Edited by: 864077 on 07-jun-2011 0:32