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.campagnebp.uploadhandler;
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.portal.service.util.AppPropertiesService;
53  import fr.paris.lutece.util.filesystem.UploadUtil;
54  
55  /**
56   *
57   */
58  public class CampagneUploadHandler extends AbstractAsynchronousUploadHandler
59  {
60      /**
61       * 
62       */
63      public static final String BEAN_NAME = "campagnebp.campagnebpUploadHandler";
64      private static final int DEFAULT_MAX_FILE_SIZE = 2097152;
65      private static final String HANDLER_NAME = "campagnebpUploadHandler";
66  
67      // Error messages
68      private static final String ERROR_MESSAGE_MULTIPLE_UPLOAD = "campagnebp.upload.message.multipleUpload";
69      private static final String ERROR_MESSAGE_AVATAR_MAX_SIZE = "campagnebp.upload.message.avatarMaxSize";
70      private static final String ERROR_MESSAGE_AVATAR_MIME_TYPE_AUTORIZED = "campagnebp.upload.message.avatarMimeTypeAutorized";
71  
72      // PROPERTY
73      private static final String PROPERTY_AVATAR_MAX_SIZE = "campagnebp.avatarMaxFileSize";
74      private static final String PROPERTY_AVATAR_EXTENSION_AUTORIZED = "campagnebp.avatarExtensionAutorized";
75  
76      // Could be just "images" and "docs"
77      /* <sessionId,<fieldName,fileItems>> */
78      /* contains uploaded file items */
79      private static Map<String, Map<String, List<FileItem>>> _mapAsynchronousUpload = new ConcurrentHashMap<String, Map<String, List<FileItem>>>( );
80  
81      @Override
82      public void addFileItemToUploadedFilesList( FileItem fileItem, String strFieldName, HttpServletRequest request )
83      {
84          // This is the name that will be displayed in the form. We keep
85          // the original name, but clean it to make it cross-platform.
86          String strFileName = UploadUtil.cleanFileName( fileItem.getName( ).trim( ) );
87  
88          initMap( request.getSession( ).getId( ), strFieldName );
89  
90          // Check if this file has not already been uploaded
91          List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, request.getSession( ) );
92  
93          if ( uploadedFiles != null )
94          {
95              boolean bNew = true;
96  
97              if ( !uploadedFiles.isEmpty( ) )
98              {
99                  Iterator<FileItem> iterUploadedFiles = uploadedFiles.iterator( );
100 
101                 while ( bNew && iterUploadedFiles.hasNext( ) )
102                 {
103                     FileItem uploadedFile = iterUploadedFiles.next( );
104                     String strUploadedFileName = UploadUtil.cleanFileName( uploadedFile.getName( ).trim( ) );
105                     // If we find a file with the same name and the same
106                     // length, we consider that the current file has
107                     // already been uploaded
108                     bNew = !( StringUtils.equals( strUploadedFileName, strFileName ) && ( uploadedFile.getSize( ) == fileItem.getSize( ) ) );
109                 }
110             }
111 
112             if ( bNew )
113             {
114                 uploadedFiles.add( fileItem );
115             }
116         }
117     }
118 
119     @Override
120     public String canUploadFiles( HttpServletRequest request, String strFieldName, List<FileItem> listFileItemsToUpload, Locale locale )
121     {
122         int nMaxSize = AppPropertiesService.getPropertyInt( PROPERTY_AVATAR_MAX_SIZE, DEFAULT_MAX_FILE_SIZE );
123         String strExtensionAuthorized = AppPropertiesService.getProperty( PROPERTY_AVATAR_EXTENSION_AUTORIZED );
124 
125         // Check if this file has not already been uploaded
126         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, request.getSession( ) );
127 
128         if ( uploadedFiles != null && uploadedFiles.size( ) >= 1 )
129         {
130             return I18nService.getLocalizedString( ERROR_MESSAGE_MULTIPLE_UPLOAD, request.getLocale( ) );
131         }
132 
133         for ( FileItem fileItem : listFileItemsToUpload )
134         {
135 
136             if ( !StringUtils.isEmpty( strExtensionAuthorized ) )
137             {
138                 boolean bMimeTypeNotAutorized = true;
139 
140                 for ( String strExtension : strExtensionAuthorized.split( "," ) )
141                 {
142                     if ( fileItem.getName( ).toLowerCase( ).endsWith( strExtension.toLowerCase( ) ) )
143                     {
144 
145                         bMimeTypeNotAutorized = false;
146                     }
147 
148                 }
149 
150                 if ( bMimeTypeNotAutorized )
151                 {
152                     return I18nService.getLocalizedString( ERROR_MESSAGE_AVATAR_MIME_TYPE_AUTORIZED, request.getLocale( ) );
153                 }
154             }
155 
156             if ( fileItem.getSize( ) > nMaxSize )
157             {
158                 return I18nService.getLocalizedString( ERROR_MESSAGE_AVATAR_MAX_SIZE, request.getLocale( ) );
159             }
160 
161         }
162 
163         return null;
164 
165     }
166 
167     @Override
168     public String getHandlerName( )
169     {
170         return HANDLER_NAME;
171     }
172 
173     @Override
174     public List<FileItem> getListUploadedFiles( String strFieldName, HttpSession session )
175     {
176         if ( StringUtils.isBlank( strFieldName ) )
177         {
178             throw new AppException( "id field name is not provided for the current file upload" );
179         }
180 
181         initMap( session.getId( ), strFieldName );
182 
183         // find session-related files in the map
184         Map<String, List<FileItem>> mapFileItemsSession = _mapAsynchronousUpload.get( session.getId( ) );
185 
186         return mapFileItemsSession.get( strFieldName );
187     }
188 
189     @Override
190     public void removeFileItem( String strFieldName, HttpSession session, int nIndex )
191     {
192         // Remove the file (this will also delete the file physically)
193         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, session );
194 
195         if ( ( uploadedFiles != null ) && !uploadedFiles.isEmpty( ) && ( uploadedFiles.size( ) > nIndex ) )
196         {
197             // Remove the object from the Hashmap
198             FileItem fileItem = uploadedFiles.remove( nIndex );
199             fileItem.delete( );
200         }
201     }
202 
203     /**
204      * Init the map Copy paste from genericAttribute AbstractGenAttUploadHandler from
205      * http://wiki.lutece.paris.fr/lutece/jsp/site/Portal.jsp?page=wiki&page_name=asynchronous_upload&view=page
206      * 
207      * @param strSessionId
208      *            the session id
209      * @param strFieldName
210      *            the field name
211      */
212     private void initMap( String strSessionId, String strFieldName )
213     {
214         // find session-related files in the map
215         Map<String, List<FileItem>> mapFileItemsSession = _mapAsynchronousUpload.get( strSessionId );
216 
217         // create map if not exists
218         if ( mapFileItemsSession == null )
219         {
220             synchronized( this )
221             {
222                 // Ignore double check locking error : assignation and instanciation of objects are separated.
223                 mapFileItemsSession = _mapAsynchronousUpload.get( strSessionId );
224 
225                 if ( mapFileItemsSession == null )
226                 {
227                     mapFileItemsSession = new ConcurrentHashMap<String, List<FileItem>>( );
228                     _mapAsynchronousUpload.put( strSessionId, mapFileItemsSession );
229                 }
230             }
231         }
232 
233         List<FileItem> listFileItems = mapFileItemsSession.get( strFieldName );
234 
235         if ( listFileItems == null )
236         {
237             listFileItems = new ArrayList<FileItem>( );
238             mapFileItemsSession.put( strFieldName, listFileItems );
239         }
240     }
241 
242     /**
243      * Removes all files associated to the session
244      * 
245      * @param strSessionId
246      *            the session id
247      */
248     public void removeSessionFiles( String strSessionId )
249     {
250         _mapAsynchronousUpload.remove( strSessionId );
251     }
252 }