001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.common.util; 017 018import java.io.BufferedInputStream; 019import java.io.BufferedOutputStream; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileOutputStream; 023import java.io.FilenameFilter; 024import java.io.IOException; 025import java.io.InputStream; 026import java.net.MalformedURLException; 027import java.net.URL; 028import java.nio.file.FileVisitResult; 029import java.nio.file.Files; 030import java.nio.file.Path; 031import java.nio.file.Paths; 032import java.nio.file.SimpleFileVisitor; 033import java.nio.file.attribute.BasicFileAttributes; 034import java.util.Objects; 035import java.util.concurrent.atomic.AtomicLong; 036import java.util.zip.ZipEntry; 037import java.util.zip.ZipInputStream; 038import java.util.zip.ZipOutputStream; 039import org.modeshape.common.annotation.Immutable; 040 041/** 042 * A set of utilities for working with files and directories. 043 */ 044@Immutable 045public class FileUtil { 046 047 private static FilenameFilter ACCEPT_ALL = new FilenameFilter() { 048 049 @Override 050 public boolean accept( File dir, 051 String name ) { 052 return true; 053 } 054 }; 055 056 /** 057 * Delete the file or directory at the supplied path. This method works on a directory that is not empty, unlike the 058 * {@link File#delete()} method. 059 * 060 * @param path the path to the file or directory that is to be deleted 061 * @return true if the file or directory at the supplied path existed and was successfully deleted, or false otherwise 062 */ 063 public static boolean delete( String path ) { 064 if (path == null || path.trim().length() == 0) return false; 065 return delete(new File(path)); 066 } 067 068 /** 069 * Delete the file or directory given by the supplied reference. This method works on a directory that is not empty, unlike 070 * the {@link File#delete()} method. 071 * 072 * @param fileOrDirectory the reference to the Java File object that is to be deleted 073 * @return true if the supplied file or directory existed and was successfully deleted, or false otherwise 074 */ 075 public static boolean delete( File fileOrDirectory ) { 076 if (fileOrDirectory == null) return false; 077 if (!fileOrDirectory.exists()) return false; 078 079 // The file/directory exists, so if a directory delete all of the contents ... 080 if (fileOrDirectory.isDirectory()) { 081 File[] files = fileOrDirectory.listFiles(); 082 if (files != null) { 083 for (File childFile : files) { 084 delete(childFile); // recursive call (good enough for now until we need something better) 085 } 086 } 087 // Now an empty directory ... 088 } 089 // Whether this is a file or empty directory, just delete it ... 090 return fileOrDirectory.delete(); 091 } 092 093 /** 094 * Copy the source file system structure into the supplied target location. If the source is a file, the destination will be 095 * created as a file; if the source is a directory, the destination will be created as a directory. 096 * 097 * @param sourceFileOrDirectory the file or directory whose contents are to be copied into the target location 098 * @param destinationFileOrDirectory the location where the copy is to be placed; does not need to exist, but if it does its 099 * type must match that of <code>src</code> 100 * @return the number of files (not directories) that were copied 101 * @throws IllegalArgumentException if the <code>src</code> or <code>dest</code> references are null 102 * @throws IOException 103 */ 104 public static int copy( File sourceFileOrDirectory, 105 File destinationFileOrDirectory ) throws IOException { 106 return copy(sourceFileOrDirectory, destinationFileOrDirectory, null); 107 } 108 109 /** 110 * Copy the source file system structure into the supplied target location. If the source is a file, the destination will be 111 * created as a file; if the source is a directory, the destination will be created as a directory. 112 * 113 * @param sourceFileOrDirectory the file or directory whose contents are to be copied into the target location 114 * @param destinationFileOrDirectory the location where the copy is to be placed; does not need to exist, but if it does its 115 * type must match that of <code>src</code> 116 * @param exclusionFilter a filter that matches files or folders that should _not_ be copied; null indicates that all files 117 * and folders should be copied 118 * @return the number of files (not directories) that were copied 119 * @throws IllegalArgumentException if the <code>src</code> or <code>dest</code> references are null 120 * @throws IOException 121 */ 122 public static int copy( File sourceFileOrDirectory, 123 File destinationFileOrDirectory, 124 FilenameFilter exclusionFilter ) throws IOException { 125 if (exclusionFilter == null) exclusionFilter = ACCEPT_ALL; 126 int numberOfFilesCopied = 0; 127 if (sourceFileOrDirectory.isDirectory()) { 128 destinationFileOrDirectory.mkdirs(); 129 String list[] = sourceFileOrDirectory.list(exclusionFilter); 130 131 for (int i = 0; i < list.length; i++) { 132 String dest1 = destinationFileOrDirectory.getPath() + File.separator + list[i]; 133 String src1 = sourceFileOrDirectory.getPath() + File.separator + list[i]; 134 135 numberOfFilesCopied += copy(new File(src1), new File(dest1), exclusionFilter); 136 } 137 } else { 138 try (FileInputStream fis = new FileInputStream(sourceFileOrDirectory); 139 BufferedInputStream bis = new BufferedInputStream(fis); 140 FileOutputStream fos = new FileOutputStream(destinationFileOrDirectory); 141 BufferedOutputStream bos = new BufferedOutputStream(fos)) { 142 int c; 143 while ((c = bis.read()) >= 0) { 144 bos.write(c); 145 } 146 } 147 numberOfFilesCopied++; 148 } 149 return numberOfFilesCopied; 150 } 151 152 /** 153 * Utility to convert {@link File} to {@link URL}. 154 * 155 * @param filePath the path of the file 156 * @return the {@link URL} representation of the file. 157 * @throws MalformedURLException 158 * @throws IllegalArgumentException if the file path is null, empty or blank 159 */ 160 public static URL convertFileToURL( String filePath ) throws MalformedURLException { 161 CheckArg.isNotEmpty(filePath, "filePath"); 162 File file = new File(filePath.trim()); 163 return file.toURI().toURL(); 164 } 165 166 /** 167 * Determines the size (in bytes) of the file or directory at the given path. 168 * 169 * @param filePath the path of the file; may not be {@code null} 170 * @return the size in bytes of the file or the total computed size of the folder. If the given path is not a valid file or 171 * folder, this will return 0. 172 * @throws IOException if anything unexpected fails. 173 */ 174 public static long size(String filePath) throws IOException { 175 CheckArg.isNotEmpty(filePath, "filePath"); 176 File file = new File(filePath); 177 if (!file.exists()) { 178 return 0; 179 } 180 if (file.isFile()) { 181 return file.length(); 182 } 183 final AtomicLong size = new AtomicLong(); 184 Files.walkFileTree(Paths.get(filePath), new SimpleFileVisitor<Path>() { 185 @Override 186 public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) { 187 size.addAndGet(attrs.size()); 188 return FileVisitResult.CONTINUE; 189 } 190 191 @Override 192 public FileVisitResult visitFileFailed( Path file, IOException exc ) { 193 return FileVisitResult.CONTINUE; 194 } 195 }); 196 return size.get(); 197 } 198 199 /** 200 * Unzip archive to the specified destination. 201 * 202 * @param zipFile zip archive 203 * @param dest directory where archive will be uncompressed 204 * @throws IOException 205 */ 206 public static void unzip(InputStream zipFile, String dest) throws IOException { 207 byte[] buffer = new byte[1024]; 208 209 //create output directory is not exists 210 File folder = new File(dest); 211 212 if (folder.exists()) { 213 FileUtil.delete(folder); 214 } 215 216 folder.mkdir(); 217 218 //get the zip file content 219 try (ZipInputStream zis = new ZipInputStream(zipFile)) { 220 //get the zipped file list entry 221 ZipEntry ze = zis.getNextEntry(); 222 File parent = new File(dest); 223 while (ze != null) { 224 String fileName = ze.getName(); 225 if (ze.isDirectory()) { 226 File newFolder = new File(parent, fileName); 227 newFolder.mkdir(); 228 } else { 229 File newFile = new File(parent, fileName); 230 try (FileOutputStream fos = new FileOutputStream(newFile)) { 231 int len; 232 while ((len = zis.read(buffer)) > 0) { 233 fos.write(buffer, 0, len); 234 } 235 } 236 } 237 ze = zis.getNextEntry(); 238 } 239 240 zis.closeEntry(); 241 } 242 } 243 244 /** 245 * Compresses directory into zip archive. 246 * 247 * @param dirName the path to the directory 248 * @param nameZipFile archive name. 249 * @throws IOException 250 */ 251 public static void zipDir(String dirName, String nameZipFile) throws IOException { 252 try (FileOutputStream fW = new FileOutputStream(nameZipFile); 253 ZipOutputStream zip = new ZipOutputStream(fW)) { 254 addFolderToZip("", dirName, zip); 255 } 256 } 257 258 /** 259 * Adds folder to the archive. 260 * 261 * @param path path to the folder 262 * @param srcFolder folder name 263 * @param zip zip archive 264 * @throws IOException 265 */ 266 public static void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) throws IOException { 267 File folder = new File(srcFolder); 268 if (folder.list().length == 0) { 269 addFileToZip(path, srcFolder, zip, true); 270 } else { 271 for (String fileName : folder.list()) { 272 if (path.equals("")) { 273 addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip, false); 274 } else { 275 addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip, false); 276 } 277 } 278 } 279 } 280 281 /** 282 * Appends file to the archive. 283 * 284 * @param path path to the file 285 * @param srcFile file name 286 * @param zip archive 287 * @param flag 288 * @throws IOException 289 */ 290 public static void addFileToZip(String path, String srcFile, ZipOutputStream zip, boolean flag) throws IOException { 291 File folder = new File(srcFile); 292 if (flag) { 293 zip.putNextEntry(new ZipEntry(path + "/" + folder.getName() + "/")); 294 } else { 295 if (folder.isDirectory()) { 296 addFolderToZip(path, srcFile, zip); 297 } else { 298 byte[] buf = new byte[1024]; 299 int len; 300 try (FileInputStream in = new FileInputStream(srcFile)) { 301 zip.putNextEntry(new ZipEntry(path + "/" + folder.getName())); 302 while ((len = in.read(buf)) > 0) { 303 zip.write(buf, 0, len); 304 } 305 } 306 } 307 } 308 } 309 310 /** 311 * Returns the extension of a file, including the dot. 312 * 313 * @param filename the name of a file, may be not be null 314 * @return the file extension or an empty string if the extension cannot be determined. 315 */ 316 public static String getExtension(final String filename) { 317 Objects.requireNonNull(filename, "filename cannot be null"); 318 int lastDotIdx = filename.lastIndexOf("."); 319 return lastDotIdx >= 0 ? filename.substring(lastDotIdx) : ""; 320 } 321 322 private FileUtil() { 323 } 324 325}