001/** 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the terms of the GNU 005 * Lesser General Public Licence. This should be distributed with the code. If 006 * you do not have a copy, see: 007 * 008 * http://www.gnu.org/copyleft/lesser.html 009 * 010 * Copyright for this code is held jointly by the individual authors. These 011 * should be listed in @author doc comments. 012 * 013 * For more information on the BioJava project and its aims, or to join the 014 * biojava-l mailing list, visit the home page at: 015 * 016 * http://www.biojava.org/ 017 * 018 * Created on Feb 23, 2012 Created by Andreas Prlic 019 * 020 * @since 3.0.2 021 */ 022package org.biojava.nbio.core.util; 023 024import java.io.File; 025import java.io.FileInputStream; 026import java.io.FileOutputStream; 027import java.io.IOException; 028import java.io.InputStream; 029import java.net.HttpURLConnection; 030import java.net.SocketTimeoutException; 031import java.net.URL; 032import java.net.URLConnection; 033import java.nio.channels.Channels; 034import java.nio.channels.FileChannel; 035import java.nio.channels.ReadableByteChannel; 036import java.nio.file.FileVisitResult; 037import java.nio.file.Files; 038import java.nio.file.Path; 039import java.nio.file.Paths; 040import java.nio.file.SimpleFileVisitor; 041import java.nio.file.attribute.BasicFileAttributes; 042 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046public class FileDownloadUtils { 047 048 private static final Logger logger = LoggerFactory.getLogger(FileDownloadUtils.class); 049 050 /** 051 * Copy the content of file src to dst TODO since java 1.7 this is provided 052 * in java.nio.file.Files 053 * 054 * @param src 055 * @param dst 056 * @throws IOException 057 */ 058 @SuppressWarnings("resource") 059 public static void copy(File src, File dst) throws IOException { 060 061 // Took following recipe from 062 // http://stackoverflow.com/questions/106770/standard-concise-way-to-copy-a-file-in-java 063 // The nio package seems to be the most efficient way to copy a file 064 FileChannel source = null; 065 FileChannel destination = null; 066 067 try { 068 // we need the supress warnings here (the warning that the stream is not closed is harmless) 069 // see http://stackoverflow.com/questions/12970407/does-filechannel-close-close-the-underlying-stream 070 source = new FileInputStream(src).getChannel(); 071 destination = new FileOutputStream(dst).getChannel(); 072 destination.transferFrom(source, 0, source.size()); 073 } finally { 074 if (source != null) { 075 source.close(); 076 } 077 if (destination != null) { 078 destination.close(); 079 } 080 } 081 } 082 083 /** 084 * Gets the file extension of a file, excluding '.'. 085 * If the file name has no extension the file name is returned. 086 * @param f a File 087 * @return The extension 088 */ 089 public static String getFileExtension(File f) { 090 String fileName = f.getName(); 091 String ext = ""; 092 int mid = fileName.lastIndexOf("."); 093 ext = fileName.substring(mid + 1, fileName.length()); 094 return ext; 095 } 096 097 /** 098 * Gets the file name up to and excluding the first 099 * '.' character. If there is no extension, the full filename 100 * is returned. 101 * @param f A file 102 * @return A possibly empty but non-null String. 103 */ 104 public static String getFilePrefix(File f) { 105 String fileName = f.getName(); 106 int mid = fileName.indexOf("."); 107 if (mid < 0) { 108 return fileName; 109 } 110 return fileName.substring(0, mid); 111 } 112 113 /** 114 * Download the content provided at URL url and store the result to a local 115 * file, using a temp file to cache the content in case something goes wrong 116 * in download. A timeout of 60 seconds is hard-coded and 10 retries are attempted. 117 * 118 * @param url 119 * @param destination 120 * @throws IOException 121 */ 122 public static void downloadFile(URL url, File destination) throws IOException { 123 int count = 0; 124 int maxTries = 10; 125 int timeout = 60000; //60 sec 126 127 File tempFile = File.createTempFile(getFilePrefix(destination), "." + getFileExtension(destination)); 128 129 // Took following recipe from stackoverflow: 130 // http://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java 131 // It seems to be the most efficient way to transfer a file 132 // See: http://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileChannel.html 133 ReadableByteChannel rbc = null; 134 FileOutputStream fos = null; 135 while (true) { 136 try { 137 URLConnection connection = prepareURLConnection(url.toString(), timeout); 138 connection.connect(); 139 InputStream inputStream = connection.getInputStream(); 140 141 rbc = Channels.newChannel(inputStream); 142 fos = new FileOutputStream(tempFile); 143 fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 144 break; 145 } catch (SocketTimeoutException e) { 146 if (++count == maxTries) throw e; 147 } finally { 148 if (rbc != null) { 149 rbc.close(); 150 } 151 if (fos != null) { 152 fos.close(); 153 } 154 } 155 } 156 157 logger.debug("Copying temp file {} to final location {}", tempFile, destination); 158 copy(tempFile, destination); 159 160 // delete the tmp file 161 tempFile.delete(); 162 163 } 164 165 /** 166 * Converts path to Unix convention and adds a terminating slash if it was 167 * omitted. 168 * 169 * @param path original platform dependent path 170 * @return path in Unix convention 171 * @author Peter Rose 172 * @since 3.2 173 */ 174 public static String toUnixPath(String path) { 175 String uPath = path; 176 if (uPath.contains("\\")) { 177 uPath = uPath.replaceAll("\\\\", "/"); 178 } 179 // this should be removed, it's need since "\" is added AtomCache code 180 if (uPath.endsWith("//")) { 181 uPath = uPath.substring(0, uPath.length() - 1); 182 } 183 if (!uPath.endsWith("/")) { 184 uPath = uPath + "/"; 185 } 186 return uPath; 187 } 188 189 /** 190 * Expands ~ in paths to the user's home directory. 191 * 192 * <p> 193 * This does not work for some special cases for paths: Other users' homes 194 * (~user/...), and Tilde expansion within the path (/.../~/...). In these cases 195 * the original argument is returned. 196 * 197 * @param file A filepath starting with a tilde 198 * @return An absolute path 199 */ 200 public static String expandUserHome(String file) { 201 // replace any / with the proper separator (/ or \ for Linux and Windows respectively). 202 file = file.replaceAll("/", "\\"+File.separator); //The "\\" is to escape the separator if needed. 203 if (file.startsWith("~") && (file.length() == 1 || File.separator.equals(file.substring(1, 2)))) { 204 file = System.getProperty("user.home") + file.substring(1); 205 } 206 return file; 207 } 208 209 /** 210 * Pings a HTTP URL. This effectively sends a HEAD request and returns 211 * <code>true</code> if the response code is in the 200-399 range. 212 * 213 * @param url The HTTP URL to be pinged. 214 * @param timeout The timeout in millis for both the connection timeout and 215 * the response read timeout. Note that the total timeout is effectively two 216 * times the given timeout. 217 * @return <code>true</code> if the given HTTP URL has returned response 218 * code 200-399 on a HEAD request within the given timeout, otherwise 219 * <code>false</code>. 220 * @author BalusC, 221 * http://stackoverflow.com/questions/3584210/preferred-java-way-to-ping-a-http-url-for-availability 222 */ 223 public static boolean ping(String url, int timeout) { 224 //url = url.replaceFirst("https", "http"); // Otherwise an exception may be thrown on invalid SSL certificates. 225 226 try { 227 HttpURLConnection connection = (HttpURLConnection) prepareURLConnection(url, timeout); 228 connection.setRequestMethod("HEAD"); 229 int responseCode = connection.getResponseCode(); 230 return (200 <= responseCode && responseCode <= 399); 231 } catch (IOException exception) { 232 return false; 233 } 234 } 235 236 /** 237 * Prepare {@link URLConnection} with customised timeouts. 238 * 239 * @param url The URL 240 * @param timeout The timeout in millis for both the connection timeout and 241 * the response read timeout. Note that the total timeout is effectively two 242 * times the given timeout. 243 * 244 * <p> 245 * Example of code. <code> 246 * UrlConnection conn = prepareURLConnection("http://www.google.com/", 20000); 247 * conn.connect(); 248 * conn.getInputStream(); 249 * </code> 250 * <p> 251 * 252 * <bold>NB. User should execute connect() method before getting input 253 * stream.</bold> 254 * @return 255 * @throws IOException 256 * @author Jacek Grzebyta 257 */ 258 public static URLConnection prepareURLConnection(String url, int timeout) throws IOException { 259 URLConnection connection = new URL(url).openConnection(); 260 connection.setReadTimeout(timeout); 261 connection.setConnectTimeout(timeout); 262 return connection; 263 } 264 265 /** 266 * Recursively delete a folder & contents 267 * 268 * @param dir directory to delete 269 */ 270 public static void deleteDirectory(Path dir) throws IOException { 271 if(dir == null || !Files.exists(dir)) 272 return; 273 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { 274 @Override 275 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 276 Files.delete(file); 277 return FileVisitResult.CONTINUE; 278 } 279 280 @Override 281 public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { 282 if (e != null) { 283 throw e; 284 } 285 Files.delete(dir); 286 return FileVisitResult.CONTINUE; 287 } 288 }); 289 } 290 /** 291 * Recursively delete a folder & contents 292 * 293 * @param dir directory to delete 294 */ 295 public static void deleteDirectory(String dir) throws IOException { 296 deleteDirectory(Paths.get(dir)); 297 } 298 299}