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  
50  /**
51   * Builds a fileItem from blobstore implementing {@link FileItem}. <br>
52   * Metadata is stored in one blob, and content in another one. get() method is lazy preventing blob to be stored in-memory. Use
53   * {@link #buildFileMetadata(String, long, String)} to build the FileMetadata.
54   * 
55   * @see #buildFileMetadata(String, long, String)
56   * @see #BlobStoreFileItem(String, IBlobStoreService)
57   *
58   */
59  public class BlobStoreFileItem implements FileItem
60  {
61      public static final String JSON_KEY_FILE_SIZE = "fileSize";
62      public static final String JSON_KEY_FILE_NAME = "fileName";
63      public static final String JSON_KEY_FILE_CONTENT_TYPE = "fileContentType";
64      public static final String JSON_KEY_FILE_BLOB_ID = "fileBlobId";
65      public static final String JSON_KEY_FILE_METADATA_BLOB_ID = "fileMetadata";
66      private static final long serialVersionUID = 1L;
67      private static Logger _logger = LogManager.getLogger( "lutece.blobstore" );
68      private final IBlobStoreService _blobstoreService;
69      private final String _strBlobId;
70      private String _strFileName;
71      private long _lFileSize;
72      private String _strFileBlobId;
73      private String _strContentType;
74  
75      /**
76       * 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
77       * used as any other FileItem.
78       * 
79       * @param strBlobId
80       *            the blob id
81       * @param blobstoreService
82       *            the blob service
83       * @throws NoSuchBlobException
84       *             if blob cannot be parsed
85       */
86      public BlobStoreFileItem( String strBlobId, IBlobStoreService blobstoreService ) throws NoSuchBlobException
87      {
88          _strBlobId = strBlobId;
89          _blobstoreService = blobstoreService;
90  
91          // first, get the metadata
92          byte [ ] blob = _blobstoreService.getBlob( _strBlobId );
93  
94          if ( blob == null )
95          {
96              throw new NoSuchBlobException( "No blob found for id " + strBlobId );
97          }
98  
99          JSONObject jsonObject = parseBlob( blob );
100 
101         if ( jsonObject != null )
102         {
103             String strSize = (String) jsonObject.get( JSON_KEY_FILE_SIZE );
104             _lFileSize = Long.parseLong( strSize );
105             _strFileName = (String) jsonObject.get( JSON_KEY_FILE_NAME );
106             // store the real blob id - file will be fetch on demand (#get)
107             _strFileBlobId = (String) jsonObject.get( JSON_KEY_FILE_BLOB_ID );
108             _strContentType = jsonObject.getString( JSON_KEY_FILE_CONTENT_TYPE );
109         }
110         else
111         {
112             throw new NoSuchBlobException( strBlobId );
113         }
114     }
115 
116     /**
117      * Gets the metadata blob id
118      * 
119      * @return the metadata blob id
120      */
121     public String getBlobId( )
122     {
123         return _strBlobId;
124     }
125 
126     /**
127      * Gets the file blob id
128      * 
129      * @return the file blob id
130      */
131     public String getFileBlobId( )
132     {
133         return _strFileBlobId;
134     }
135 
136     /**
137      * Deletes both blobs : metadata <strong>AND</strong> content.
138      */
139     @Override
140     public void delete( )
141     {
142         _blobstoreService.delete( _strFileBlobId );
143         _blobstoreService.delete( _strBlobId );
144     }
145 
146     /**
147      * {@inheritDoc}
148      */
149     @Override
150     public byte [ ] get( )
151     {
152         return _blobstoreService.getBlob( _strFileBlobId );
153     }
154 
155     /**
156      * {@inheritDoc}
157      */
158     @Override
159     public String getContentType( )
160     {
161         return _strContentType;
162     }
163 
164     /**
165      * Not supported
166      * 
167      * @return null
168      */
169     @Override
170     public String getFieldName( )
171     {
172         return null;
173     }
174 
175     /**
176      * {@inheritDoc}
177      * 
178      * @throws IOException
179      *             ioexception
180      */
181     @Override
182     public InputStream getInputStream( ) throws IOException
183     {
184         return _blobstoreService.getBlobInputStream( _strFileBlobId );
185     }
186 
187     /**
188      * {@inheritDoc}
189      */
190     @Override
191     public String getName( )
192     {
193         return _strFileName;
194     }
195 
196     /**
197      * Not supported - throws UnsupportedOperationException exception
198      * 
199      * @return nothing
200      * @throws IOException
201      *             ioe
202      */
203     @Override
204     public OutputStream getOutputStream( ) throws IOException
205     {
206         throw new UnsupportedOperationException( );
207     }
208 
209     /**
210      * {@inheritDoc}
211      */
212     @Override
213     public long getSize( )
214     {
215         return _lFileSize;
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     @Override
222     public String getString( )
223     {
224         return new String( get( ) );
225     }
226 
227     /**
228      * {@inheritDoc}
229      */
230     @Override
231     public String getString( String encoding ) throws UnsupportedEncodingException
232     {
233         return new String( get( ), encoding );
234     }
235 
236     /**
237      * Not supported
238      * 
239      * @return false
240      */
241     @Override
242     public boolean isFormField( )
243     {
244         return false;
245     }
246 
247     /**
248      * Always false.
249      * 
250      * @return false
251      */
252     @Override
253     public boolean isInMemory( )
254     {
255         return false;
256     }
257 
258     /**
259      * Not supported
260      * 
261      * @param name
262      *            -
263      */
264     @Override
265     public void setFieldName( String name )
266     {
267         // nothing
268     }
269 
270     /**
271      * Not supported
272      * 
273      * @param state
274      *            -
275      */
276     @Override
277     public void setFormField( boolean state )
278     {
279         // nothing
280     }
281 
282     /**
283      * Not supported
284      * 
285      * @param file
286      *            -
287      * @throws Exception
288      *             ex
289      */
290     @Override
291     public void write( File file ) throws Exception
292     {
293         throw new UnsupportedOperationException( );
294     }
295 
296     /**
297      * {@inheritDoc}
298      */
299     @Override
300     public String toString( )
301     {
302         return "BlobId:" + _strBlobId + " FileBlobId:" + _strFileBlobId + " FileName:" + _strFileName;
303     }
304 
305     /**
306      * Parses a blob to a JSONObject
307      * 
308      * @param blob
309      *            the blob
310      * @return the {@link JSONObject}, <code>null</code> if blob is null or an exception occur
311      */
312     private static JSONObject parseBlob( byte [ ] blob )
313     {
314         if ( blob == null )
315         {
316             return null;
317         }
318 
319         try
320         {
321             return JSONObject.fromObject( new String( blob ) );
322         }
323         catch( JSONException je )
324         {
325             _logger.error( je.getMessage( ), je );
326         }
327 
328         return null;
329     }
330 
331     /**
332      * Builds the json value of a file metadata.
333      * 
334      * @param strFileName
335      *            filename
336      * @param lSize
337      *            size
338      * @param strFileBlobId
339      *            the blob id
340      * @param strContentType
341      *            the content type
342      * @return the json of the fileMetadata to store in BlobStore
343      */
344     public static final String buildFileMetadata( String strFileName, long lSize, String strFileBlobId, String strContentType )
345     {
346         JSONObject json = new JSONObject( );
347         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_SIZE, Long.toString( lSize ) );
348         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_NAME, strFileName );
349         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_BLOB_ID, strFileBlobId );
350         json.accumulate( BlobStoreFileItem.JSON_KEY_FILE_CONTENT_TYPE, strContentType );
351 
352         return json.toString( );
353     }
354 }