View Javadoc
1   /*
2    * Copyright (c) 2002-2020, 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.lutecetools.service;
35  
36  import java.util.ArrayList;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Locale;
40  import java.util.Map;
41  import java.util.concurrent.ConcurrentHashMap;
42  
43  import javax.servlet.http.HttpServletRequest;
44  import javax.servlet.http.HttpSession;
45  
46  import org.apache.commons.fileupload.FileItem;
47  import org.apache.commons.lang.StringUtils;
48  
49  import fr.paris.lutece.plugins.asynchronousupload.service.AbstractAsynchronousUploadHandler;
50  import fr.paris.lutece.portal.service.i18n.I18nService;
51  import fr.paris.lutece.portal.service.util.AppException;
52  import fr.paris.lutece.util.filesystem.UploadUtil;
53  
54  public class LutecetoolsAsynchronousUploadHandler extends AbstractAsynchronousUploadHandler
55  {
56  
57      private static final String HANDLER_NAME = "lutecetoolsAsynchronousUploadHandler";
58      public static final String BEAN_NAME = "lutecetools.lutecetoolsAsynchronousUploadHandler";
59  
60      private static final String ERROR_MESSAGE_UNKNOWN_ERROR = "lutecetools.message.unknownError";
61  
62      private static Map<String, Map<String, List<FileItem>>> _mapWebtiersUpload = new ConcurrentHashMap<>( );
63  
64      /**
65       * Vérifie si une liste de fichiers uploadés pour un champ donné sont valides
66       * 
67       * @param request               La requête effectuée
68       * @param strFieldName          Le nom du champ ayant servi à uploadé un
69       *                              fichier.
70       * @param listFileItemsToUpload Liste des fichiers uploadés à verifier.
71       * @param locale                La locale à utiliser pour afficher les messages
72       *                              d'erreurs éventuels
73       * @return Le message d'erreur, ou null si aucune erreur n'a été détéctée et si
74       *         les fichiers sont valides.
75       */
76      @Override
77      public String canUploadFiles( HttpServletRequest request, String strFieldName, List<FileItem> listFileItemsToUpload,
78              Locale locale )
79      {
80          if ( StringUtils.isNotBlank( strFieldName ) )
81          {
82              initMap( request.getSession( ).getId( ), strFieldName );
83              List<FileItem> listUploadedFileItems = getListUploadedFiles( strFieldName, request.getSession( ) );
84  
85              if ( !listFileItemsToUpload.isEmpty( ) && listFileItemsToUpload.size( ) == 1
86                      && listUploadedFileItems.isEmpty( )
87                      && compareTo( "text/", listFileItemsToUpload.get( 0 ).getContentType( ) ) )
88              {
89                  return null;
90              }
91          }
92          return I18nService.getLocalizedString( ERROR_MESSAGE_UNKNOWN_ERROR, locale );
93      }
94  
95      /**
96       * Permet de récupérer la liste des fichiers uploadés pour un champ donné. La
97       * liste doit être ordonnée chronologiquement par date d'upload. A chaque
98       * fichier un index sera associé correspondant à l'index du fichier dans la
99       * liste (le fichier le plus vieux aura l'index 0). Deux appels successifs de
100      * cette méthode doivent donc renvoyés une liste ordonnées de la même manière.
101      * 
102      * @param strFieldName Le nom du champ dont on souhaite récupérer les fichiers
103      * @param session      la session de l'utilisateur utilisant le fichier. A
104      *                     n'utiliser que si les fichiers sont enregistrés en
105      *                     session.
106      * @return La liste des fichiers uploadés pour le champ donné
107      */
108     @Override
109     public List<FileItem> getListUploadedFiles( String strFieldName, HttpSession session )
110     {
111         if ( StringUtils.isBlank( strFieldName ) )
112         {
113             throw new AppException( "id field name is not provided for the current file upload" );
114         }
115 
116         initMap( session.getId( ), strFieldName );
117 
118         // find session-related files in the map
119         Map<String, List<FileItem>> mapFileItemsSession = _mapWebtiersUpload.get( session.getId( ) );
120 
121         return mapFileItemsSession.get( strFieldName );
122     }
123 
124     /**
125      * Permet de supprimer un fichier précédament uploadé
126      * 
127      * @param strFieldName Le nom du champ
128      * @param session      la session de l'utilisateur utilisant le fichier. A
129      *                     n'utiliser que si les fichiers sont enregistrés en
130      *                     session.
131      * @param nIndex       L'index du fichier dans la liste des fichiers uploadés.
132      */
133     @Override
134     public void removeFileItem( String strFieldName, HttpSession session, int nIndex )
135     {
136         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, session );
137 
138         if ( ( uploadedFiles != null ) && !uploadedFiles.isEmpty( ) && ( uploadedFiles.size( ) > nIndex ) )
139         {
140             // Remove the object from the Hashmap
141             FileItem fileItem = uploadedFiles.remove( nIndex );
142             fileItem.delete( );
143         }
144     }
145 
146     /**
147      * Permet de déclarer un fichier comme uploadé. L'implémentation de cette
148      * méthode est désormais en charge de la gestion du fichier.
149      * 
150      * @param fileItem     Le fichier uploadé
151      * @param strFieldName Le nom du champ auquel le fichier est associé
152      * @param request      La requête
153      */
154     @Override
155     public void addFileItemToUploadedFilesList( FileItem fileItem, String strFieldName, HttpServletRequest request )
156     {
157         // This is the name that will be displayed in the form. We keep
158         // the original name, but clean it to make it cross-platform.
159         String strFileName = UploadUtil.cleanFileName( fileItem.getName( ).trim( ) );
160 
161         initMap( request.getSession( ).getId( ), strFieldName );
162 
163         // Check if this file has not already been uploaded
164         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, request.getSession( ) );
165 
166         if ( uploadedFiles != null )
167         {
168             boolean bNew = true;
169 
170             if ( !uploadedFiles.isEmpty( ) )
171             {
172                 Iterator<FileItem> iterUploadedFiles = uploadedFiles.iterator( );
173 
174                 while ( bNew && iterUploadedFiles.hasNext( ) )
175                 {
176                     FileItem uploadedFile = iterUploadedFiles.next( );
177                     String strUploadedFileName = UploadUtil.cleanFileName( uploadedFile.getName( ).trim( ) );
178                     // If we find a file with the same name and the same
179                     // length, we consider that the current file has
180                     // already been uploaded
181                     bNew = !( StringUtils.equals( strUploadedFileName, strFileName )
182                             && ( uploadedFile.getSize( ) == fileItem.getSize( ) ) );
183                 }
184             }
185 
186             if ( bNew )
187             {
188                 uploadedFiles.add( fileItem );
189             }
190         }
191     }
192 
193     /**
194      * Permet de définir le nom du handler. Ce nom doit être unique, et ne contenir
195      * que des caractères numériques (pas de points, de virgule, ...). Il est
196      * recommandé de préfixer le nom du plugin, puis de suffixer un nom fonctionnel.
197      * Attention, le nom du handler est différent du nom du bean Spring associé !
198      */
199     @Override
200     public String getHandlerName( )
201     {
202         return HANDLER_NAME;
203     }
204 
205     /**
206      * Init the map
207      * 
208      * @param strSessionId the session id
209      * @param strFieldName the field name
210      */
211     private void initMap( String strSessionId, String strFieldName )
212     {
213         // find session-related files in the map
214         Map<String, List<FileItem>> mapFileItemsSession = _mapWebtiersUpload.get( strSessionId );
215 
216         // create map if not exists
217         if ( mapFileItemsSession == null )
218         {
219             synchronized ( this )
220             {
221                 // Ignore double check locking error : assignation and instanciation of objects
222                 // are separated.
223                 mapFileItemsSession = _mapWebtiersUpload.get( strSessionId );
224 
225                 if ( mapFileItemsSession == null )
226                 {
227                     mapFileItemsSession = new ConcurrentHashMap<>( );
228                     _mapWebtiersUpload.put( strSessionId, mapFileItemsSession );
229                 }
230             }
231         }
232 
233         List<FileItem> listFileItems = mapFileItemsSession.get( strFieldName );
234 
235         if ( listFileItems == null )
236         {
237             listFileItems = new ArrayList<>( );
238             mapFileItemsSession.put( strFieldName, listFileItems );
239         }
240     }
241 
242     private boolean compareTo( String mime, String fileContent )
243     {
244         // create for check mime type upload image in createImage
245         String s = fileContent.substring( 0, mime.length( ) );
246         return s.equals( mime );
247     }
248 
249     public boolean hasFile( HttpServletRequest request, String strFieldName )
250     {
251         if ( StringUtils.isNotBlank( strFieldName ) )
252         {
253             List<FileItem> listUploadedFileItems = getListUploadedFiles( strFieldName, request.getSession( ) );
254 
255             if ( !listUploadedFileItems.isEmpty( ) )
256             {
257                 return true;
258             }
259         }
260         return false;
261     }
262 
263     public FileItem getFile( HttpServletRequest request, String strFieldName )
264     {
265         if ( StringUtils.isNotBlank( strFieldName ) )
266         {
267             List<FileItem> listUploadedFileItems = getListUploadedFiles( strFieldName, request.getSession( ) );
268 
269             if ( !listUploadedFileItems.isEmpty( ) )
270             {
271                 return listUploadedFileItems.get( 0 );
272             }
273         }
274         return null;
275     }
276 
277 }