/*
* Copyright 2014 Loic Merckel
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.uploader.drive.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import java.nio.file.*;
import java.util.ArrayDeque;
import java.util.Queue;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.nio.file.FileVisitResult.*;
public class FileUtils {
final static Logger logger = LoggerFactory.getLogger(FileUtils.class);
private FileUtils () { super () ; } ;
public static final String emptyMd5 = "d41d8cd98f00b204e9800998ecf8427e" ;
public static String getMD5 (File file) throws IOException
{
if (file.isDirectory())
return emptyMd5 ;
HashCode hc = com.google.common.io.Files.hash(file, Hashing.md5());
return (hc != null) ? (hc.toString()) : (null) ;
}
public static String readAllAndgetMD5 (InputStream in) throws IOException
{
com.google.common.hash.HashingInputStream his = null ;
try
{
his = new com.google.common.hash.HashingInputStream (Hashing.md5(), in) ;
final int bufferSize = 2097152 ;
final ReadableByteChannel inputChannel = Channels.newChannel(his);
final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
while (inputChannel.read(buffer) != -1) {
buffer.clear();
}
/*
byte[] bytesBuffer = new byte[bufferSize] ;
int r = his.read(bytesBuffer, 0, bufferSize) ;
while (r != -1)
r = his.read(bytesBuffer) ;
*/
HashCode hc = his.hash() ;
return (hc != null) ? (hc.toString()) : (null) ;
}
finally
{
if (his != null)
his.close() ;
}
}
public enum FileFinderOption {
ALL,
FILE_ONLY,
DIRECTORY_ONLY,
}
public static Queue<Path> getAllFilesPath (Path srcDir, FileFinderOption option) throws IOException
{
Queue<Path> queue = new ArrayDeque<Path> () ;
if (srcDir == null || !Files.isDirectory(srcDir))
return queue ;
TreePathFinder tpf = new TreePathFinder(queue, option);
Files.walkFileTree(srcDir, tpf);
return queue ;
}
public static BasicFileAttributes getFileAttr (Path path) throws IOException
{
BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class) ;
return attr ;
}
private static class TreePathFinder extends SimpleFileVisitor<Path> {
private final Queue<Path> filesPath ;
private final FileFinderOption option ;
public TreePathFinder(Queue<Path> filesPath, FileFinderOption option) {
super();
if (filesPath == null)
throw new AssertionError ("TreePathFinder cannot accept null queue.") ;
this.option = option ;
this.filesPath = filesPath;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attr)
{
if (option == FileFinderOption.ALL || option == FileFinderOption.FILE_ONLY) {
filesPath.add(file) ;
}
return CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
{
if (option == FileFinderOption.ALL || option == FileFinderOption.DIRECTORY_ONLY) {
filesPath.add(dir) ;
}
return CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
return CONTINUE;
}
// If there is some error accessing
// the file, let the user know.
// If you don't override this method
// and an error occurs, an IOException
// is thrown.
/*
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
if (exc instanceof FileSystemLoopException) {
logger.error("Error occurred while walking the directory to find all the files path (cycle detected: " + file + ").", exc);
} else {
logger.error("Error occurred while walking the directory to find all the files path.", exc);
}
return CONTINUE;
}
*/
}
public static class InputStreamProgressFilter extends FilterInputStream
{
public static interface StreamProgressCallback
{
public void onStreamProgress (double progress) ;
}
private final long size ;
private volatile long read = 0 ;
private final StreamProgressCallback callback ;
protected InputStreamProgressFilter(InputStream in, long size, StreamProgressCallback callback) {
super(in);
if (size < 0)
throw new AssertionError ("The size of the stream to be minitored must be positive") ;
// Note: we need the size because, in.available() does not necessarily return the size (see specs).
this.size = size ;
this.callback = callback ;
checkProgress () ;
}
private double getProgress ()
{
if (size == 0)
return 1.0 ;
return read / (double)size ;
}
private void checkProgress ()
{
if (callback == null)
return ;
callback.onStreamProgress(getProgress());
}
@Override
public int read() throws IOException {
int r = super.read();
read += r ;
checkProgress () ;
return r;
}
@Override
public int read(byte[] b) throws IOException {
int r = super.read(b);
read += r ;
checkProgress () ;
return r;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int r = super.read(b, off, len);
read += r ;
checkProgress () ;
return r;
}
@Override
public long skip(long n) throws IOException {
long r = super.skip(n);
read += r ;
checkProgress () ;
return r;
}
}
public static InputStream getInputStreamWithProgressFilter(InputStreamProgressFilter.StreamProgressCallback callback, long size, Path path) throws FileNotFoundException
{
return getInputStreamWithProgressFilter (callback, size, new FileInputStream(path.toFile())) ;
}
public static InputStream getInputStreamWithProgressFilter(InputStreamProgressFilter.StreamProgressCallback callback, long size, InputStream input)
{
if (input == null)
return null ;
InputStream in = new BufferedInputStream(
new InputStreamProgressFilter(input, size, callback));
return in;
}
public static void saveInputStreamInFile (InputStream input, File target, boolean closeInputStream) throws IOException
{
if (input == null || target == null)
throw new AssertionError () ;
FileOutputStream output = new FileOutputStream(target);
try
{
IOUtils.copyLarge(input, output);
}
finally
{
if (output != null)
output.close();
if (closeInputStream)
input.close();
}
}
// http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
public static String humanReadableByteCount(long bytes, boolean si) {
int unit = si ? 1000 : 1024;
if (bytes < unit)
return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
}