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.util.http;
35  
36  import fr.paris.lutece.portal.service.html.EncodingService;
37  import fr.paris.lutece.portal.service.util.AppLogService;
38  import fr.paris.lutece.portal.web.upload.MultipartHttpServletRequest;
39  import fr.paris.lutece.portal.web.upload.NormalizeFileItem;
40  
41  import org.apache.commons.fileupload.FileItem;
42  import org.apache.commons.fileupload.FileItemIterator;
43  import org.apache.commons.fileupload.FileItemStream;
44  import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
45  import org.apache.commons.fileupload.FileUploadException;
46  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
47  import org.apache.commons.fileupload.servlet.ServletFileUpload;
48  import org.apache.commons.lang3.StringUtils;
49  
50  import java.io.UnsupportedEncodingException;
51  
52  import java.util.ArrayList;
53  import java.util.HashMap;
54  import java.util.List;
55  import java.util.Map;
56  import java.util.Optional;
57  
58  import javax.servlet.http.HttpServletRequest;
59  
60  /**
61   *
62   * MultipartUtil
63   *
64   */
65  public final class MultipartUtil
66  {
67      /**
68       * Private constructor
69       */
70      private MultipartUtil( )
71      {
72      }
73  
74      /**
75       * Check if the given HTTP request has multipart content
76       *
77       * @param request
78       *            the HTTP request
79       * @return true if it has multipart content, false otherwise
80       */
81      public static boolean isMultipart( HttpServletRequest request )
82      {
83          return ServletFileUpload.isMultipartContent( request );
84      }
85  
86      /**
87       * Convert a HTTP request to a {@link MultipartHttpServletRequest}
88       *
89       * @param nSizeThreshold
90       *            the size threshold
91       * @param nRequestSizeMax
92       *            the request size max
93       * @param bActivateNormalizeFileName
94       *            true if the file name must be normalized, false otherwise
95       * @param request
96       *            the HTTP request
97       * @return a {@link MultipartHttpServletRequest}, null if the request does not have a multipart content
98       * @throws SizeLimitExceededException
99       *             exception if the file size is too big
100      * @throws FileUploadException
101      *             exception if an unknown error has occurred
102      */
103     public static MultipartHttpServletRequest convert( int nSizeThreshold, long nRequestSizeMax, boolean bActivateNormalizeFileName,
104             HttpServletRequest request ) throws FileUploadException
105     {
106         if ( !isMultipart( request ) )
107         {
108             return null;
109         }
110 
111         // Create a factory for disk-based file items
112         DiskFileItemFactory factory = new DiskFileItemFactory( );
113 
114         // Set factory constraints
115         factory.setSizeThreshold( nSizeThreshold );
116 
117         // Create a new file upload handler
118         ServletFileUpload upload = new ServletFileUpload( factory );
119 
120         // Set overall request size constraint
121         upload.setSizeMax( nRequestSizeMax );
122 
123         // get encoding to be used
124         String strEncoding = Optional.ofNullable( request.getCharacterEncoding( ) ).orElse( EncodingService.getEncoding( ) );
125 
126         Map<String, List<FileItem>> mapFiles = new HashMap<>( );
127         Map<String, String [ ]> mapParameters = new HashMap<>( );
128 
129         List<FileItem> listItems;
130 
131         try {
132             listItems = upload.parseRequest(request);
133         }
134         catch( SizeLimitExceededException e )
135         {
136             closeInputStream( upload, request );
137             throw e;
138         }
139 
140         // Process the uploaded items
141         for ( FileItem item : listItems )
142         {
143             processItem( item, strEncoding, bActivateNormalizeFileName, mapFiles, mapParameters );
144         }
145 
146         return new MultipartHttpServletRequest( request, mapFiles, mapParameters );
147     }
148 
149     /**
150      * Close the inputStream of the request
151      * @param upload
152      * @param request
153      */
154     private static void closeInputStream( ServletFileUpload upload, HttpServletRequest request)
155     {
156         try {
157             upload.setSizeMax(-1);
158             FileItemIterator fileiterator = upload.getItemIterator(request);
159 
160             while (fileiterator.hasNext()) 
161             {
162                 FileItemStream item = fileiterator.next();
163                 item.openStream().close();
164             }
165         }
166         catch (Exception e)
167         {
168             AppLogService.error( "error occured during closing the stream" , e);
169         }
170 
171     }
172 
173     private static void processItem( FileItem item, String strEncoding, boolean bActivateNormalizeFileName, Map<String, List<FileItem>> mapFiles,
174             Map<String, String [ ]> mapParameters )
175     {
176         if ( item.isFormField( ) )
177         {
178             String strValue = StringUtils.EMPTY;
179 
180             if ( item.getSize( ) > 0 )
181             {
182                 try
183                 {
184                     strValue = item.getString( strEncoding );
185                 }
186                 catch( UnsupportedEncodingException ex )
187                 {
188                     // if encoding problem, try with system encoding
189                     strValue = item.getString( );
190                 }
191             }
192 
193             // check if item of same name already in map
194             String [ ] curParam = mapParameters.get( item.getFieldName( ) );
195 
196             if ( curParam == null )
197             {
198                 // simple form field
199                 mapParameters.put( item.getFieldName( ), new String [ ] {
200                         strValue
201                 } );
202             }
203             else
204             {
205                 // array of simple form fields
206                 String [ ] newArray = new String [ curParam.length + 1];
207                 System.arraycopy( curParam, 0, newArray, 0, curParam.length );
208                 newArray [curParam.length] = strValue;
209                 mapParameters.put( item.getFieldName( ), newArray );
210             }
211         }
212         else
213         {
214             // multipart file field, if the parameter filter ActivateNormalizeFileName is
215             // set to true
216             // all file name will be normalize
217             FileItem fileItem = bActivateNormalizeFileName ? new NormalizeFileItem( item ) : item;
218             List<FileItem> listFileItem = mapFiles.get( fileItem.getFieldName( ) );
219 
220             if ( listFileItem != null )
221             {
222                 listFileItem.add( fileItem );
223             }
224             else
225             {
226                 listFileItem = new ArrayList<>( 1 );
227                 listFileItem.add( fileItem );
228                 mapFiles.put( fileItem.getFieldName( ), listFileItem );
229             }
230         }
231     }
232 }