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.file;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.nio.file.Files;
40  import java.nio.file.Path;
41  import java.util.zip.ZipEntry;
42  import java.util.zip.ZipOutputStream;
43  
44  import org.apache.commons.lang3.StringUtils;
45  
46  import fr.paris.lutece.portal.service.util.AppLogService;
47  import fr.paris.lutece.portal.service.util.AppPropertiesService;
48  import fr.paris.lutece.util.string.StringUtil;
49  
50  /**
51   * Utility class for files
52   */
53  public final class FileUtil
54  {
55      public static final String CONSTANT_MIME_TYPE_ZIP = "application/zip";
56      public static final String CONSTANT_MIME_TYPE_CSV = "application/csv";
57      public static final String EXTENSION_ZIP = ".zip";
58      public static final String EXTENSION_CSV = ".csv";
59      private static final String PROPERTY_ALLOWED_IMAGES_EXTENSIONS = "lutece.files.allowedImagesExtentions";
60      private static final String PROPERTY_ALLOWED_HTML_EXTENSIONS = "lutece.files.allowedHtmlExtentions";
61      private static final String DEFAULT_IMAGES_EXTENSION = "webp,png,jpg,jpeg,svg";
62      private static final String DEFAULT_HTML_EXTENSION = "html,htm,xhtml";
63      private static final String FREEMARKER_EXTENSION = "ftl";
64      private static final String CONSTANT_POINT = ".";
65      private static final String CONSTANT_COMMA = ",";
66  
67      /**
68       * Private constructor
69       */
70      private FileUtil( )
71      {
72      }
73  
74      /**
75       * Check if the extension of the file is an image extension
76       * 
77       * @param strImageFileName
78       *            The file name to check
79       * @return True if the extension is correct, false otherwise
80       */
81      public static boolean hasImageExtension( String strImageFileName )
82      {
83          String strImagesExtentions = AppPropertiesService.getProperty( PROPERTY_ALLOWED_IMAGES_EXTENSIONS, DEFAULT_IMAGES_EXTENSION );
84  
85          return hasExtension( strImageFileName, strImagesExtentions );
86      }
87  
88      /**
89       * Check if a file has a valid html extension
90       * 
91       * @param strFileName
92       *            The file name to check
93       * @return True if the file name is valid, false otherwise
94       */
95      public static boolean hasHtmlExtension( String strFileName )
96      {
97          String strImagesExtentions = AppPropertiesService.getProperty( PROPERTY_ALLOWED_HTML_EXTENSIONS, DEFAULT_HTML_EXTENSION );
98  
99          return hasExtension( strFileName, strImagesExtentions );
100     }
101 
102     /**
103      * Check if a file has a valid Freemarker extension
104      * 
105      * @param strFileName
106      *            The file name to check
107      * @return True if the file name is valid, false otherwise
108      */
109     public static boolean hasFreemarkerExtension( String strFileName )
110     {
111         return hasExtension( strFileName, FREEMARKER_EXTENSION );
112     }
113 
114     /**
115      * Check if a file name match extensions in a given list
116      * 
117      * @param strFileName
118      *            The file name to check
119      * @param strAllowedExtensions
120      *            The comma separated list of allowed extensions
121      * @return True of the extension of the file exists in the extension list
122      */
123     private static boolean hasExtension( String strFileName, String strAllowedExtensions )
124     {
125         int nIndex = strFileName.lastIndexOf( CONSTANT_POINT );
126 
127         if ( ( nIndex >= 0 ) && ( strFileName.length( ) > ( nIndex + 2 ) ) )
128         {
129             String strExtension = strFileName.substring( nIndex + 1 );
130 
131             if ( StringUtils.isNotEmpty( strExtension ) )
132             {
133                 for ( String strAllowedExtention : strAllowedExtensions.split( CONSTANT_COMMA ) )
134                 {
135                     if ( StringUtils.equalsIgnoreCase( strExtension, strAllowedExtention ) )
136                     {
137                         return true;
138                     }
139                 }
140             }
141         }
142 
143         return false;
144     }
145 
146     /**
147      * creates a zip file
148      * 
149      * @param zipFile
150      *            the zipfile to create
151      * @param files
152      *            to add to the Zip
153      * @throws IOException
154      */
155     public static void zipFiles( Path zipFile, Path... paths ) throws IOException
156     {
157         if ( zipFile.toFile( ).exists( ) )
158         {
159             deleteFile( zipFile.toFile( ) );
160         }
161 
162         try ( ZipOutputStream zos = new ZipOutputStream( Files.newOutputStream( zipFile ) ) )
163         {
164             for ( Path file : paths )
165             {
166                 addEntryToZip( zos, file );
167             }
168         }
169     }
170 
171     private static void addEntryToZip( ZipOutputStream zos, Path file ) throws IOException
172     {
173         try ( InputStream fis = Files.newInputStream( file ) )
174         {
175             ZipEntry zipEntry = new ZipEntry( file.toFile( ).getName( ) );
176             zos.putNextEntry( zipEntry );
177 
178             byte [ ] bytes = new byte [ 1024];
179             int length;
180             while ( ( length = fis.read( bytes ) ) >= 0 )
181             {
182                 zos.write( bytes, 0, length );
183             }
184             zos.closeEntry( );
185         }
186     }
187 
188     /**
189      * Converts French diacritics characters into non diacritics. <br />
190      * Replace whitespaces by underscores <br />
191      * Keep only underscores and alphanum characters <br />
192      * Transform to lowercase
193      * 
194      * @param string
195      * @return
196      */
197     public static final String normalizeFileName( String string )
198     {
199         return StringUtil.replaceAccent( string ).replace( ' ', '_' ).replaceAll( "[^a-zA-Z0-9_]+", "" ).toLowerCase( );
200     }
201 
202     /**
203      * Delete the file. <br />
204      * Logs an error if the delete is not succesful.
205      * 
206      * @param pathname
207      */
208     public static void deleteFile( File file )
209     {
210         if ( file == null )
211         {
212             AppLogService.error( "Error deleting file, file null" );
213             return;
214         }
215         try
216         {
217             if ( file.exists( ) )
218             {
219                 Files.delete( file.toPath( ) );
220             }
221         }
222         catch( IOException e )
223         {
224             AppLogService.error( "Error deleting file", e );
225         }
226     }
227 
228 }