001 /* 002 * Copyright (c) 2011, Cloudera, Inc. All Rights Reserved. 003 * 004 * Cloudera, Inc. licenses this file to you under the Apache License, 005 * Version 2.0 (the "License"). You may not use this file except in 006 * compliance with the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * This software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 011 * CONDITIONS OF ANY KIND, either express or implied. See the License for 012 * the specific language governing permissions and limitations under the 013 * License. 014 */ 015 package com.cloudera.lib.io; 016 017 import com.cloudera.lib.util.Check; 018 019 import java.io.File; 020 import java.io.FileInputStream; 021 import java.io.IOException; 022 import java.io.InputStream; 023 import java.io.OutputStream; 024 import java.io.Reader; 025 import java.io.StringWriter; 026 import java.io.Writer; 027 import java.text.MessageFormat; 028 import java.util.zip.ZipEntry; 029 import java.util.zip.ZipOutputStream; 030 031 public abstract class IOUtils { 032 033 /** 034 * Deletes the specified path recursively. 035 * <p/> 036 * As a safety mechanism, it converts the specified file to an absolute 037 * path and it throws an exception if the path is shorter than 5 character 038 * (you dont want to delete things like /tmp, /usr, /etc, /var, /opt, /sbin, /dev). 039 * 040 * @param file path to delete. 041 * @throws IOException thrown if an IO error occurred. 042 */ 043 public static void delete(File file) throws IOException { 044 if (file.getAbsolutePath().length() < 5) { 045 throw new IllegalArgumentException( 046 MessageFormat.format("Path [{0}] is too short, not deleting", file.getAbsolutePath())); 047 } 048 if (file.exists()) { 049 if (file.isDirectory()) { 050 File[] children = file.listFiles(); 051 if (children != null) { 052 for (File child : children) { 053 delete(child); 054 } 055 } 056 } 057 if (!file.delete()) { 058 throw new RuntimeException(MessageFormat.format("Could not delete path [{0}]", file.getAbsolutePath())); 059 } 060 } 061 } 062 063 /** 064 * Copies an inputstream to an outputstream. 065 * 066 * @param is inputstream to copy. 067 * @param os target outputstream. 068 * @throws IOException thrown if an IO error occurred. 069 */ 070 public static void copy(InputStream is, OutputStream os) throws IOException { 071 Check.notNull(is, "is"); 072 Check.notNull(os, "os"); 073 copy(is, os, 0, -1); 074 } 075 076 /** 077 * Copies a range of bytes from an inputstream to an outputstream. 078 * 079 * @param is inputstream to copy. 080 * @param os target outputstream. 081 * @param offset of the inputstream to start the copy from, the offset 082 * is relative to the current position. 083 * @param len length of the inputstream to copy, <code>-1</code> means 084 * until the end of the inputstream. 085 * @throws IOException thrown if an IO error occurred. 086 */ 087 public static void copy(InputStream is, OutputStream os, long offset, long len) throws IOException { 088 Check.notNull(is, "is"); 089 Check.notNull(os, "os"); 090 byte[] buffer = new byte[1024]; 091 long skipped = is.skip(offset); 092 if (skipped == offset) { 093 if (len == -1) { 094 int read = is.read(buffer); 095 while (read > -1) { 096 os.write(buffer, 0, read); 097 read = is.read(buffer); 098 } 099 is.close(); 100 } 101 else { 102 long count = 0; 103 int read = is.read(buffer); 104 while (read > -1 && count < len) { 105 count += read; 106 if (count < len) { 107 os.write(buffer, 0, read); 108 read = is.read(buffer); 109 } 110 else if (count == len) { 111 os.write(buffer, 0, read); 112 } 113 else { 114 int leftToWrite = read - (int) (count - len); 115 os.write(buffer, 0, leftToWrite); 116 } 117 } 118 } 119 os.flush(); 120 } 121 else { 122 throw new IOException(MessageFormat.format("InputStream ended before offset [{0}]", offset)); 123 } 124 } 125 126 /** 127 * Copies a reader to a writer. 128 * 129 * @param reader reader to copy. 130 * @param writer target writer. 131 * @throws IOException thrown if an IO error occurred. 132 */ 133 public static void copy(Reader reader, Writer writer) throws IOException { 134 Check.notNull(reader, "reader"); 135 Check.notNull(writer, "writer"); 136 copy(reader, writer, 0, -1); 137 } 138 139 /** 140 * Copies a range of chars from a reader to a writer. 141 * 142 * @param reader reader to copy. 143 * @param writer target writer. 144 * @param offset of the reader to start the copy from, the offset 145 * is relative to the current position. 146 * @param len length of the reader to copy, <code>-1</code> means 147 * until the end of the reader. 148 * @throws IOException thrown if an IO error occurred. 149 */ 150 public static void copy(Reader reader, Writer writer, long offset, long len) throws IOException { 151 Check.notNull(reader, "reader"); 152 Check.notNull(writer, "writer"); 153 Check.ge0(offset, "offset"); 154 char[] buffer = new char[1024]; 155 long skipped = reader.skip(offset); 156 if (skipped == offset) { 157 if (len == -1) { 158 int read = reader.read(buffer); 159 while (read > -1) { 160 writer.write(buffer, 0, read); 161 read = reader.read(buffer); 162 } 163 reader.close(); 164 } 165 else { 166 long count = 0; 167 int read = reader.read(buffer); 168 while (read > -1 && count < len) { 169 count += read; 170 if (count < len) { 171 writer.write(buffer, 0, read); 172 read = reader.read(buffer); 173 } 174 else if (count == len) { 175 writer.write(buffer, 0, read); 176 } 177 else { 178 int leftToWrite = read - (int) (count - len); 179 writer.write(buffer, 0, leftToWrite); 180 } 181 } 182 } 183 writer.flush(); 184 } 185 else { 186 throw new IOException(MessageFormat.format("Reader ended before offset [{0}]", offset)); 187 } 188 } 189 190 /** 191 * Reads a reader into a string. 192 * <p/> 193 * 194 * @param reader reader to read. 195 * @return string with the contents of the reader. 196 * @throws IOException thrown if an IO error occurred. 197 */ 198 public static String toString(Reader reader) throws IOException { 199 Check.notNull(reader, "reader"); 200 StringWriter writer = new StringWriter(); 201 copy(reader, writer); 202 return writer.toString(); 203 } 204 205 206 /** 207 * Zips the contents of a directory into a zip outputstream. 208 * 209 * @param dir directory contents to zip. 210 * @param relativePath relative path top prepend to all files in the zip. 211 * @param zos zip output stream 212 * @throws IOException thrown if an IO error occurred. 213 */ 214 public static void zipDir(File dir, String relativePath, ZipOutputStream zos) throws IOException { 215 Check.notNull(dir, "dir"); 216 Check.notNull(relativePath, "relativePath"); 217 Check.notNull(zos, "zos"); 218 zipDir(dir, relativePath, zos, true); 219 zos.close(); 220 } 221 222 /** 223 * This recursive method is used by the {@link #zipDir(File, String, ZipOutputStream)} method. 224 * <p/> 225 * A special handling is required for the start of the zip (via the start parameter). 226 * 227 * @param dir directory contents to zip. 228 * @param relativePath relative path top prepend to all files in the zip. 229 * @param zos zip output stream 230 * @param start indicates if this invocation is the start of the zip. 231 * @throws IOException thrown if an IO error occurred. 232 */ 233 private static void zipDir(File dir, String relativePath, ZipOutputStream zos, boolean start) throws IOException { 234 String[] dirList = dir.list(); 235 for (String aDirList : dirList) { 236 File f = new File(dir, aDirList); 237 if (!f.isHidden()) { 238 if (f.isDirectory()) { 239 if (!start) { 240 ZipEntry dirEntry = new ZipEntry(relativePath + f.getName() + "/"); 241 zos.putNextEntry(dirEntry); 242 zos.closeEntry(); 243 } 244 String filePath = f.getPath(); 245 File file = new File(filePath); 246 zipDir(file, relativePath + f.getName() + "/", zos, false); 247 } 248 else { 249 ZipEntry anEntry = new ZipEntry(relativePath + f.getName()); 250 zos.putNextEntry(anEntry); 251 InputStream is = new FileInputStream(f); 252 byte[] arr = new byte[4096]; 253 int read = is.read(arr); 254 while (read > -1) { 255 zos.write(arr, 0, read); 256 read = is.read(arr); 257 } 258 is.close(); 259 zos.closeEntry(); 260 } 261 } 262 } 263 } 264 265 }