View Javadoc
1   /*
2    * Copyright (c) 2002-2022, 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.dansmarue.modules.rest.service.upload.handler;
35  
36  import java.io.IOException;
37  import java.nio.file.Files;
38  import java.nio.file.Path;
39  import java.nio.file.Paths;
40  import java.util.ArrayList;
41  import java.util.Iterator;
42  import java.util.List;
43  import java.util.Locale;
44  import java.util.Map;
45  import java.util.concurrent.ConcurrentHashMap;
46  
47  import javax.servlet.http.HttpServletRequest;
48  import javax.servlet.http.HttpSession;
49  
50  import org.apache.commons.fileupload.FileItem;
51  import org.apache.commons.io.FilenameUtils;
52  import org.apache.commons.lang.ArrayUtils;
53  import org.apache.commons.lang.StringUtils;
54  import org.apache.commons.logging.Log;
55  import org.apache.commons.logging.LogFactory;
56  import org.springframework.beans.factory.annotation.Value;
57  
58  import fr.paris.lutece.plugins.asynchronousupload.service.AbstractAsynchronousUploadHandler;
59  import fr.paris.lutece.portal.service.i18n.I18nService;
60  import fr.paris.lutece.portal.service.util.AppException;
61  import fr.paris.lutece.util.filesystem.UploadUtil;
62  
63  /**
64   * AbstractUploadHandler.
65   */
66  public abstract class AbstractUploadHandler extends AbstractAsynchronousUploadHandler
67  {
68  
69      /** The upload directory. */
70      @Value( "${signalement.directory.upload}" )
71      protected String _uploadDirectory;
72  
73      /** The max file size. */
74      @Value( "${signalement.upload.max.file.size}" )
75      private String _maxFileSize;
76  
77      /** The max category files count. */
78      @Value( "${signalement.upload.max.category.files.count}" )
79      private Integer _maxCategoryFilesCount;
80  
81      /**
82       * Gets the max file size.
83       *
84       * @return the max file size
85       */
86      public String getMaxFileSize( )
87      {
88          return _maxFileSize;
89      }
90  
91      /** The Constant PREFIX_ENTRY_ID. */
92      private static final String PREFIX_ENTRY_ID = "dansmarue_";
93  
94      /** The Constant LOGGER. */
95      private static final Log LOGGER = LogFactory.getLog( AbstractUploadHandler.class );
96  
97      /** The Constant ERROR_EXTENSION. */
98      public static final String ERROR_EXTENSION = "module.dansmarue.rest.message.upload.file.error.extension";
99  
100     /** The Constant ERROR_FILES_COUNT. */
101     public static final String ERROR_FILES_COUNT = "module.dansmarue.rest.message.upload.file.error.files.count";
102 
103     /** The Constant HANDLER_NAME. */
104     private static final String HANDLER_NAME = "DansmarueAsynchronousUploadHandler";
105 
106     /** The extension list. */
107     private static String[] _extensionList = { "jpeg", "png", "jpg" };
108 
109     /**
110      * Constructor.
111      */
112     public AbstractUploadHandler( )
113     {
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public void addFileItemToUploadedFilesList( FileItem fileItem, String strFieldName, HttpServletRequest request )
121     {
122         String strFileName = UploadUtil.cleanFileName( fileItem.getName( ).trim( ) );
123 
124         initMap( request.getSession( ), PREFIX_ENTRY_ID + strFieldName );
125 
126         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, request.getSession( ) );
127 
128         if ( uploadedFiles != null )
129         {
130             boolean bNew = true;
131 
132             if ( !uploadedFiles.isEmpty( ) )
133             {
134                 Iterator<FileItem> iterUploadedFiles = uploadedFiles.iterator( );
135 
136                 while ( bNew && iterUploadedFiles.hasNext( ) )
137                 {
138                     FileItem uploadedFile = iterUploadedFiles.next( );
139                     String strUploadedFileName = UploadUtil.cleanFileName( uploadedFile.getName( ).trim( ) );
140                     bNew = !( StringUtils.equals( strUploadedFileName, strFileName ) && ( uploadedFile.getSize( ) == fileItem.getSize( ) ) );
141                 }
142             }
143 
144             if ( bNew )
145             {
146                 uploadedFiles.add( fileItem );
147             }
148         }
149     }
150 
151     /**
152      * {@inheritDoc}
153      */
154     @Override
155     public String canUploadFiles( HttpServletRequest request, String strFieldName, List<FileItem> listFileItemsToUpload, Locale locale )
156     {
157         if ( StringUtils.isNotBlank( strFieldName ) )
158         {
159 
160             if ( getListUploadedFiles( strFieldName, request.getSession( ) ).size( ) >= _maxCategoryFilesCount )
161             {
162                 return I18nService.getLocalizedString( ERROR_FILES_COUNT, new Object [ ] {
163                         _maxCategoryFilesCount
164                 }, locale );
165             }
166 
167             for ( FileItem fileItem : listFileItemsToUpload )
168             {
169                 if ( !ArrayUtils.contains( _extensionList, FilenameUtils.getExtension( fileItem.getName( ).toLowerCase( ) ) ) )
170                 {
171                     return I18nService.getLocalizedString( ERROR_EXTENSION, locale ) + StringUtils.join( _extensionList, ", " );
172                 }
173             }
174         }
175         return null;
176     }
177 
178     /**
179      * {@inheritDoc}
180      */
181     @Override
182     public String getHandlerName( )
183     {
184 
185         return HANDLER_NAME;
186     }
187 
188     /**
189      * return picture file.
190      *
191      * @param request
192      *            the http request.
193      * @param strFieldName
194      *            the field name.
195      * @return the file item
196      */
197     public FileItem getFile( HttpServletRequest request, String strFieldName )
198     {
199         if ( StringUtils.isNotBlank( strFieldName ) )
200         {
201             List<FileItem> listUploadedFileItems = getListUploadedFiles( strFieldName, request.getSession( ) );
202 
203             if ( !listUploadedFileItems.isEmpty( ) )
204             {
205                 return listUploadedFileItems.get( 0 );
206             }
207         }
208         return null;
209     }
210 
211     /**
212      * {@inheritDoc}
213      */
214     @Override
215     public List<FileItem> getListUploadedFiles( String strFieldName, HttpSession session )
216     {
217         if ( StringUtils.isBlank( strFieldName ) )
218         {
219             throw new AppException( "id field name is not provided for the current file upload" );
220         }
221 
222         initMap( session, strFieldName );
223 
224         Map<String, List<FileItem>> mapFileItemsSession = getMapAsynchronousUpload( ).get( session.getId( ) );
225 
226         return mapFileItemsSession.get( strFieldName );
227     }
228 
229     /**
230      * {@inheritDoc}
231      */
232     @Override
233     public void removeFileItem( String strFieldName, HttpSession session, int nIndex )
234     {
235         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, session );
236         remove( strFieldName, session, nIndex, uploadedFiles );
237 
238     }
239 
240     /**
241      * Inits the map.
242      *
243      * @param session
244      *            the session
245      * @param strFieldName
246      *            the str field name
247      */
248     private void initMap( HttpSession session, String strFieldName )
249     {
250         String strSessionId = session.getId( );
251         Map<String, List<FileItem>> mapFileItemsSession = getMapAsynchronousUpload( ).get( strSessionId );
252 
253         if ( mapFileItemsSession == null )
254         {
255             synchronized( this )
256             {
257                 mapFileItemsSession = getMapAsynchronousUpload( ).get( strSessionId );
258 
259                 if ( mapFileItemsSession == null )
260                 {
261                     mapFileItemsSession = new ConcurrentHashMap<>( );
262                     getMapAsynchronousUpload( ).put( strSessionId, mapFileItemsSession );
263                 }
264             }
265         }
266 
267         mapFileItemsSession.computeIfAbsent( strFieldName, k -> new ArrayList<>( ) );
268     }
269 
270     /**
271      * Removes the file.
272      *
273      * @param session
274      *            the session
275      * @param fileToSave
276      *            the file to save
277      * @param categoryName
278      *            the category name
279      */
280     private void removeFile( HttpSession session, FileItem fileToSave, String categoryName )
281     {
282         String id = getIdInSession( session );
283         if ( ( null == id ) || ( null == fileToSave ) || StringUtils.isEmpty( categoryName ) )
284         {
285             return;
286         }
287 
288         String categoryPath = FilenameUtils.concat( getUploadDirectory( ), id );
289         categoryPath = FilenameUtils.concat( categoryPath, categoryName );
290 
291         String fileName = FilenameUtils.getName( fileToSave.getName( ) );
292         try
293         {
294             String filePath = FilenameUtils.concat( categoryPath, fileName );
295             Path path = Paths.get( filePath );
296             Files.deleteIfExists( path );
297 
298             LOGGER.info( "Fichier supprimé avec succès : " + fileName );
299         }
300         catch( IOException ex )
301         {
302             LOGGER.error( "Impossible de supprimer le fichier : " + fileName, ex );
303         }
304     }
305 
306     /**
307      * Removes the.
308      *
309      * @param strFieldName
310      *            the str field name
311      * @param session
312      *            the session
313      * @param nIndex
314      *            the n index
315      * @param uploadedFiles
316      *            the uploaded files
317      */
318     private void remove( String strFieldName, HttpSession session, int nIndex, List<FileItem> uploadedFiles )
319     {
320         if ( ( uploadedFiles != null ) && !uploadedFiles.isEmpty( ) && ( uploadedFiles.size( ) > nIndex ) )
321         {
322             FileItem fileItem = uploadedFiles.remove( nIndex );
323             removeFile( session, fileItem, strFieldName );
324             fileItem.delete( );
325         }
326     }
327 
328     /**
329      * Checks for file.
330      *
331      * @param request
332      *            the request
333      * @param strFieldName
334      *            the str field name
335      * @return true, if successful
336      */
337     public boolean hasFile( HttpServletRequest request, String strFieldName )
338     {
339         if ( StringUtils.isNotBlank( strFieldName ) )
340         {
341             List<FileItem> listUploadedFileItems = getListUploadedFiles( strFieldName, request.getSession( ) );
342 
343             if ( !listUploadedFileItems.isEmpty( ) )
344             {
345                 return true;
346             }
347         }
348         return false;
349     }
350 
351     /**
352      * Gets the id in session.
353      *
354      * @param session
355      *            the session
356      * @return the id in session
357      */
358     public abstract String getIdInSession( HttpSession session );
359 
360     /**
361      * Gets the upload directory.
362      *
363      * @return the upload directory
364      */
365     public abstract String getUploadDirectory( );
366 
367     /**
368      * Gets the map asynchronous upload.
369      *
370      * @return the map asynchronous upload
371      */
372     abstract Map<String, Map<String, List<FileItem>>> getMapAsynchronousUpload( );
373 }