View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.blobstore.service;
35  
36  import net.sf.json.JSONException;
37  import net.sf.json.JSONObject;
38  
39  import org.apache.commons.fileupload.FileItem;
40  
41  import org.apache.logging.log4j.LogManager;
42  import org.apache.logging.log4j.Logger;
43  
44  import java.io.File;
45  import java.io.IOException;
46  import java.io.InputStream;
47  import java.io.OutputStream;
48  import java.io.UnsupportedEncodingException;
49  import org.apache.commons.fileupload.FileItemHeaders;
50  
51  /**
52   * Builds a fileItem from blobstore implementing {@link FileItem}. <br>
53   * Metadata is stored in one blob, and content in another one. get() method is lazy preventing blob to be stored in-memory. Use
54   * {@link #buildFileMetadata(String, long, String)} to build the FileMetadata.
55   * 
56   * @see #buildFileMetadata(String, long, String)
57   * @see #BlobStoreFileItem(String, IBlobStoreService)
58   *
59   */
60  public class BlobStoreFileItem implements FileItem
61  {
62      public static final String JSON_KEY_FILE_SIZE = "fileSize";
63      public static final String JSON_KEY_FILE_NAME = "fileName";
64      public static final String JSON_KEY_FILE_CONTENT_TYPE = "fileContentType";
65      public static final String JSON_KEY_FILE_BLOB_ID = "fileBlobId";
66      public static final String JSON_KEY_FILE_METADATA_BLOB_ID = "fileMetadata";
67      private static final long serialVersionUID = 1L;
68      private static Logger _logger = LogManager.getLogger( "lutece.blobstore" );
69      private final IBlobStoreService _blobstoreService;
70      private final String _strBlobId;
71      private String _strFileName;
72      private long _lFileSize;
73      private String _strFileBlobId;
74      private String _strContentType;
75  
76      /**
77       * Builds a fileItem from blobstore. get() method is lazy. The {@link IBlobStoreService} is here to prevent specific usage for the fileItem so it can be
78       * used as any other FileItem.
79       * 
80       * @param strBlobId
81       *            the blob id
82       * @param blobstoreService
83       *            the blob service
84       * @throws NoSuchBlobException
85       *             if blob cannot be parsed
86       */
87      public BlobStoreFileItem( String strBlobId, IBlobStoreService blobstoreService ) throws NoSuchBlobException
88      {
89          _strBlobId = strBlobId;
90          _blobstoreService = blobstoreService;
91  
92          // first, get the metadata
93          byte [ ] blob = _blobstoreService.getBlob( _strBlobId );
94  
95          if ( blob == null )
96          {
97              throw new NoSuchBlobException( "No blob found for id " + strBlobId );
98          }
99  
100         JSONObject jsonObject = parseBlob( blob );
101 
102         if ( jsonObject != null )
103         {
104             String strSize = (String) jsonObject.get( JSON_KEY_FILE_SIZE );
105             _lFileSize = Long.parseLong( strSize );
106             _strFileName = (String) jsonObject.get( JSON_KEY_FILE_NAME );
107             // store the real blob id - file will be fetch on demand (#get)
108             _strFileBlobId = (String) jsonObject.get( JSON_KEY_FILE_BLOB_ID );
109             _strContentType = jsonObject.getString( JSON_KEY_FILE_CONTENT_TYPE );
110         }
111         else
112         {
113             throw new NoSuchBlobException( strBlobId );
114         }
115     }
116 
117     /**
118      * Gets the metadata blob id
119      * 
120      * @return the metadata blob id
121      */
122     public String getBlobId( )
123     {
124         return _strBlobId;
125     }
126 
127     /**
128      * Gets the file blob id
129      * 
130      * @return the file blob id
131      */
132     public String getFileBlobId( )
133     {
134         return _strFileBlobId;
135     }
136 
137     /**
138      * Deletes both blobs : metadata <strong>AND</strong> content.
139      */
140     @Override
141     public void delete( )
142     {
143         _blobstoreService.delete( _strFileBlobId );
144         _blobstoreService.delete( _strBlobId );
145     }
146 
147     /**
148      * {@inheritDoc}
149      */
150     @Override
151     public byte [ ] get( )
152     {
153         return _blobstoreService.getBlob( _strFileBlobId );
154     }
155 
156     /**
157      * {@inheritDoc}
158      */
159     @Override
160     public String getContentType( )
161     {
162         return _strContentType;
163     }
164 
165     /**
166      * Not supported
167      * 
168      * @return null
169      */
170     @Override
171     public String getFieldName( )
172     {
173         return null;
174     }
175 
176     /**
177      * {@inheritDoc}
178      * 
179      * @throws IOException
180      *             ioexception
181      */
182     @Override
183     public InputStream getInputStream( ) throws IOException
184     {
185         return _blobstoreService.getBlobInputStream( _strFileBlobId );
186     }
187 
188     /**
189      * {@inheritDoc}
190      */
191     @Override
192     public String getName( )
193     {
194         return _strFileName;
195     }
196 
197     /**
198      * Not supported - throws UnsupportedOperationException exception
199      * 
200      * @return nothing
201      * @throws IOException
202      *             ioe
203      */
204     @Override
205     public OutputStream getOutputStream( ) throws IOException
206     {
207         throw new UnsupportedOperationException( );
208     }
209 
210     /**
211      * {@inheritDoc}
212      */
213     @Override
214     public long getSize( )
215     {
216         return _lFileSize;
217     }
218 
219     /**
220      * {@inheritDoc}
221      */
222     @Override
223     public String getString( )
224     {
225         return new String( get( ) );
226     }
227 
228     /**
229      * {@inheritDoc}
230      */
231     @Override
232     public String getString( String encoding ) throws UnsupportedEncodingException
233     {
234         return new String( get( ), encoding );
235     }
236 
237     /**
238      * Not supported
239      * 
240      * @return false
241      */
242     @Override
243     public boolean isFormField( )
244     {
245         return false;
246     }
247 
248     /**
249      * Always false.
250      * 
251      * @return false
252      */
253     @Override
254     public boolean isInMemory( )
255     {
256         return false;
257     }
258 
259     /**
260      * Not supported
261      * 
262      * @param name
263      *            -
264      */
265     @Override
266     public void setFieldName( String name )
267     {
268         // nothing
269     }
270 
271     /**
272      * Not supported
273      * 
274      * @param state
275      *            -
276      */
277     @Override
278     public void setFormField( boolean state )
279     {
280         // nothing
281     }
282 
283     /**
284      * Not supported
285      * 
286      * @param file
287      *            -
288      * @throws Exception
289      *             ex
290      */
291     @Override
292     public void write( File file ) throws Exception
293     {
294         throw new UnsupportedOperationException( );
295     }
296 
297     /**
298      * {@inheritDoc}
299      */
300     @Override
301     public String toString( )
302     {
303         return "BlobId:" + _strBlobId + " FileBlobId:" + _strFileBlobId + " FileName:" + _strFileName;
304     }
305 
306     /**
307      * Parses a blob to a JSONObject
308      * 
309      * @param blob
310      *            the blob
311      * @return the {@link JSONObject}, <code>null</code> if blob is null or an exception occur
312      */
313     private static JSONObject parseBlob( byte [ ] blob )
314     {
315         if ( blob == null )
316         {
317             return null;
318         }
319 
320         try
321         {
322             return JSONObject.fromObject( new String( blob ) );
323         }
324         catch( JSONException je )
325         {
326             _logger.error( je.getMessage( ), je );
327         }
328 
329         return null;
330     }
331 
332     /**
333      * Builds the json value of a file metadata.
334      * 
335      * @param strFileName
336      *            filename
337      * @param lSize
338      *            size
339      * @param strFileBlobId
340      *            the blob id
341      * @param strContentType
342      *            the content type
343      * @return the json of the fileMetadata to store in BlobStore
344      */
345     public static final String buildFileMetadata( String strFileName, long lSize, String strFileBlobId, String strContentType )
346     {
347         JSONObject json = new JSONObject( );
348         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_SIZE, Long.toString( lSize ) );
349         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_NAME, strFileName );
350         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_BLOB_ID, strFileBlobId );
351         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_CONTENT_TYPE, strContentType );
352 
353         return json.toString( );
354     }
355 
356     @Override
357     public FileItemHeaders getHeaders() {
358         throw new UnsupportedOperationException("Not supported yet.");
359     }
360 
361     @Override
362     public void setHeaders(FileItemHeaders fih) {
363         throw new UnsupportedOperationException("Not supported yet.");
364     }
365 }