// Reset this field in case it was set to true for the previous file
isCheckingIntegrity = false;
// Throw a specific FileTransferException if source and destination files are identical
if(sourceFile.equalsCanonical(destFile))
throw new FileTransferException(FileTransferException.SOURCE_AND_DESTINATION_IDENTICAL);
// Determine whether or not AbstractFile.copyRemotelyTo() should be used to copy the file.
// Some file protocols do not provide a getOutputStream() method and require the use of copyRemotelyTo(). Some other
// may also offer server to server copy which is more efficient than stream copy.
boolean copied = false;
if(sourceFile.isFileOperationSupported(FileOperation.COPY_REMOTELY)) {
try {
sourceFile.copyRemotelyTo(destFile);
copied = true;
}
catch(IOException e) {
// The file will be copied manually
}
}
// If the file wasn't copied using copyRemotelyTo(), or if copyRemotelyTo() failed
InputStream in = null;
if(!copied) {
// Copy source file stream to destination file
try {
long inLength = sourceFile.getSize();
// Try to open InputStream
try {
long destFileSize = destFile.getSize();
if(append && destFileSize!=-1) {
in = sourceFile.getInputStream(destFileSize);
// Do not calculate checksum, as it needs to be calculated on the whole file
inLength -= destFileSize;
// Increase current file ByteCounter by the number of bytes skipped
currentFileByteCounter.add(destFileSize);
// Increase skipped ByteCounter by the number of bytes skipped
currentFileSkippedByteCounter.add(destFileSize);
}
else {
in = sourceFile.getInputStream();
if(integrityCheckEnabled)
in = new ChecksumInputStream(in, MessageDigest.getInstance(CHECKSUM_VERIFICATION_ALGORITHM));
}
setCurrentInputStream(in);
}
catch(Exception e) {
LOGGER.debug("IOException caught, throwing FileTransferException", e);
throw new FileTransferException(FileTransferException.OPENING_SOURCE);
}
// Copy source stream to destination file
destFile.copyStream(tlin, append, inLength);
}
finally {
// This block will always be executed, even if an exception
// was thrown in the catch block
// Tries to close the streams no matter what happened before
closeCurrentInputStream();
}
}
// Preserve source file's date
if(destFile.isFileOperationSupported(FileOperation.CHANGE_DATE)) {
try {
destFile.changeDate(sourceFile.getDate());
}
catch (IOException e) {
LOGGER.debug("failed to change the date of "+destFile, e);
// Fail silently
}
}
// Preserve source file's permissions: preserve only the permissions bits that are supported by the source file
// and use default permissions for the rest of them.
if(destFile.isFileOperationSupported(FileOperation.CHANGE_PERMISSION)) {
try {
destFile.importPermissions(sourceFile, FilePermissions.DEFAULT_FILE_PERMISSIONS); // use #importPermissions(AbstractFile, int) to avoid isDirectory test
}
catch(IOException e) {
LOGGER.debug("failed to import "+sourceFile+" permissions into "+destFile, e);
// Fail silently
}
}
// Under Mac OS X only, preserving the file type and creator
if(OsFamily.MAC_OS_X.isCurrent()
&& sourceFile.hasAncestor(LocalFile.class)
&& destFile.hasAncestor(LocalFile.class)) {
String sourcePath = sourceFile.getAbsolutePath();
try {
FileManager.setFileTypeAndCreator(destFile.getAbsolutePath(), FileManager.getFileType(sourcePath), FileManager.getFileCreator(sourcePath));
}
catch(IOException e) {
// Swallow the exception and do not interrupt the transfer
LOGGER.debug("Error while setting Mac OS X file type and creator on destination", e);
}
}
// This block is executed only if integrity check has been enabled (disabled by default)
if(integrityCheckEnabled) {
String sourceChecksum;
String destinationChecksum;
// Indicate that integrity is being checked, the value is reset when the next file starts
isCheckingIntegrity = true;
if(in!=null && (in instanceof ChecksumInputStream)) {
// The file was copied with a ChecksumInputStream, the checksum is already calculated, simply
// retrieve it
sourceChecksum = ((ChecksumInputStream)in).getChecksumString();
}
else {
// The file was copied using AbstractFile#copyRemotelyTo(), or the transfer was resumed:
// we have to calculate the source file's checksum from scratch.
try {
sourceChecksum = calculateChecksum(sourceFile);
}
catch(Exception e) {
throw new FileTransferException(FileTransferException.READING_SOURCE);
}
}
LOGGER.debug("Source checksum= "+sourceChecksum);
// Calculate the destination file's checksum
try {
destinationChecksum = calculateChecksum(destFile);
}
catch(Exception e) {
throw new FileTransferException(FileTransferException.READING_DESTINATION);
}
LOGGER.debug("Destination checksum= "+destinationChecksum);
// Compare both checksums and throw an exception if they don't match
if(!sourceChecksum.equals(destinationChecksum)) {
throw new FileTransferException(FileTransferException.CHECKSUM_MISMATCH);
}
}
}