MultipartUtil.java

  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. import fr.paris.lutece.portal.service.html.EncodingService;
  36. import fr.paris.lutece.portal.service.util.AppLogService;
  37. import fr.paris.lutece.portal.web.upload.MultipartHttpServletRequest;
  38. import fr.paris.lutece.portal.web.upload.NormalizeFileItem;

  39. import org.apache.commons.fileupload.FileItem;
  40. import org.apache.commons.fileupload.FileItemIterator;
  41. import org.apache.commons.fileupload.FileItemStream;
  42. import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
  43. import org.apache.commons.fileupload.FileUploadException;
  44. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  45. import org.apache.commons.fileupload.servlet.ServletFileUpload;
  46. import org.apache.commons.lang3.StringUtils;

  47. import java.io.UnsupportedEncodingException;

  48. import java.util.ArrayList;
  49. import java.util.HashMap;
  50. import java.util.List;
  51. import java.util.Map;
  52. import java.util.Optional;

  53. import javax.servlet.http.HttpServletRequest;

  54. /**
  55.  *
  56.  * MultipartUtil
  57.  *
  58.  */
  59. public final class MultipartUtil
  60. {
  61.     /**
  62.      * Private constructor
  63.      */
  64.     private MultipartUtil( )
  65.     {
  66.     }

  67.     /**
  68.      * Check if the given HTTP request has multipart content
  69.      *
  70.      * @param request
  71.      *            the HTTP request
  72.      * @return true if it has multipart content, false otherwise
  73.      */
  74.     public static boolean isMultipart( HttpServletRequest request )
  75.     {
  76.         return ServletFileUpload.isMultipartContent( request );
  77.     }

  78.     /**
  79.      * Convert a HTTP request to a {@link MultipartHttpServletRequest}
  80.      *
  81.      * @param nSizeThreshold
  82.      *            the size threshold
  83.      * @param nRequestSizeMax
  84.      *            the request size max
  85.      * @param bActivateNormalizeFileName
  86.      *            true if the file name must be normalized, false otherwise
  87.      * @param request
  88.      *            the HTTP request
  89.      * @return a {@link MultipartHttpServletRequest}, null if the request does not have a multipart content
  90.      * @throws SizeLimitExceededException
  91.      *             exception if the file size is too big
  92.      * @throws FileUploadException
  93.      *             exception if an unknown error has occurred
  94.      */
  95.     public static MultipartHttpServletRequest convert( int nSizeThreshold, long nRequestSizeMax, boolean bActivateNormalizeFileName,
  96.             HttpServletRequest request ) throws FileUploadException
  97.     {
  98.         if ( !isMultipart( request ) )
  99.         {
  100.             return null;
  101.         }

  102.         // Create a factory for disk-based file items
  103.         DiskFileItemFactory factory = new DiskFileItemFactory( );

  104.         // Set factory constraints
  105.         factory.setSizeThreshold( nSizeThreshold );

  106.         // Create a new file upload handler
  107.         ServletFileUpload upload = new ServletFileUpload( factory );

  108.         // Set overall request size constraint
  109.         upload.setSizeMax( nRequestSizeMax );

  110.         // get encoding to be used
  111.         String strEncoding = Optional.ofNullable( request.getCharacterEncoding( ) ).orElse( EncodingService.getEncoding( ) );

  112.         Map<String, List<FileItem>> mapFiles = new HashMap<>( );
  113.         Map<String, String [ ]> mapParameters = new HashMap<>( );

  114.         List<FileItem> listItems;

  115.         try {
  116.             listItems = upload.parseRequest(request);
  117.         }
  118.         catch( SizeLimitExceededException e )
  119.         {
  120.             closeInputStream( upload, request );
  121.             throw e;
  122.         }

  123.         // Process the uploaded items
  124.         for ( FileItem item : listItems )
  125.         {
  126.             processItem( item, strEncoding, bActivateNormalizeFileName, mapFiles, mapParameters );
  127.         }

  128.         return new MultipartHttpServletRequest( request, mapFiles, mapParameters );
  129.     }

  130.     /**
  131.      * Close the inputStream of the request
  132.      * @param upload
  133.      * @param request
  134.      */
  135.     private static void closeInputStream( ServletFileUpload upload, HttpServletRequest request)
  136.     {
  137.         try {
  138.             upload.setSizeMax(-1);
  139.             FileItemIterator fileiterator = upload.getItemIterator(request);

  140.             while (fileiterator.hasNext())
  141.             {
  142.                 FileItemStream item = fileiterator.next();
  143.                 item.openStream().close();
  144.             }
  145.         }
  146.         catch (Exception e)
  147.         {
  148.             AppLogService.error( "error occured during closing the stream" , e);
  149.         }

  150.     }

  151.     private static void processItem( FileItem item, String strEncoding, boolean bActivateNormalizeFileName, Map<String, List<FileItem>> mapFiles,
  152.             Map<String, String [ ]> mapParameters )
  153.     {
  154.         if ( item.isFormField( ) )
  155.         {
  156.             String strValue = StringUtils.EMPTY;

  157.             if ( item.getSize( ) > 0 )
  158.             {
  159.                 try
  160.                 {
  161.                     strValue = item.getString( strEncoding );
  162.                 }
  163.                 catch( UnsupportedEncodingException ex )
  164.                 {
  165.                     // if encoding problem, try with system encoding
  166.                     strValue = item.getString( );
  167.                 }
  168.             }

  169.             // check if item of same name already in map
  170.             String [ ] curParam = mapParameters.get( item.getFieldName( ) );

  171.             if ( curParam == null )
  172.             {
  173.                 // simple form field
  174.                 mapParameters.put( item.getFieldName( ), new String [ ] {
  175.                         strValue
  176.                 } );
  177.             }
  178.             else
  179.             {
  180.                 // array of simple form fields
  181.                 String [ ] newArray = new String [ curParam.length + 1];
  182.                 System.arraycopy( curParam, 0, newArray, 0, curParam.length );
  183.                 newArray [curParam.length] = strValue;
  184.                 mapParameters.put( item.getFieldName( ), newArray );
  185.             }
  186.         }
  187.         else
  188.         {
  189.             // multipart file field, if the parameter filter ActivateNormalizeFileName is
  190.             // set to true
  191.             // all file name will be normalize
  192.             FileItem fileItem = bActivateNormalizeFileName ? new NormalizeFileItem( item ) : item;
  193.             List<FileItem> listFileItem = mapFiles.get( fileItem.getFieldName( ) );

  194.             if ( listFileItem != null )
  195.             {
  196.                 listFileItem.add( fileItem );
  197.             }
  198.             else
  199.             {
  200.                 listFileItem = new ArrayList<>( 1 );
  201.                 listFileItem.add( fileItem );
  202.                 mapFiles.put( fileItem.getFieldName( ), listFileItem );
  203.             }
  204.         }
  205.     }
  206. }