Skip to content

Commit

Permalink
Refactoring transfer method
Browse files Browse the repository at this point in the history
  • Loading branch information
dkocher committed Feb 9, 2007
1 parent b079e5f commit ceaf53f
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 75 deletions.
33 changes: 33 additions & 0 deletions source/ch/cyberduck/core/AbstractStreamListener.java
@@ -0,0 +1,33 @@
package ch.cyberduck.core;

/*
* Copyright (c) 2007 David Kocher. All rights reserved.
* http://cyberduck.ch/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* dkocher@cyberduck.ch
*/

/**
* @version $Id$
*/
public class AbstractStreamListener implements StreamListener {

public void bytesSent(int bytes) {
;
}

public void bytesReceived(int bytes) {
;
}
}
31 changes: 31 additions & 0 deletions source/ch/cyberduck/core/IOResumeException.java
@@ -0,0 +1,31 @@
package ch.cyberduck.core;

/*
* Copyright (c) 2007 David Kocher. All rights reserved.
* http://cyberduck.ch/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* dkocher@cyberduck.ch
*/

import java.io.IOException;

/**
* @version $Id$
*/
public class IOResumeException extends IOException {

public IOResumeException(String s) {
super(s);
}
}
16 changes: 14 additions & 2 deletions source/ch/cyberduck/core/Local.java
Expand Up @@ -102,7 +102,7 @@ public Local(File path) {
public boolean createNewFile() {
try {
if(super.createNewFile()) {
this.setProgress(0);
this.setIcon(0);
}
}
catch(IOException e) {
Expand Down Expand Up @@ -214,7 +214,16 @@ public String getAbsolute() {

private final static Object lock = new Object();

public void setProgress(int progress) {
/**
* Update the custom icon for the file in the Finder
* @param progress An integer from -1 and 9. If -1 is passed,
* the resource fork with the custom icon is removed from the file.
*/
public void setIcon(int progress) {
if(progress > 9 || progress < -1) {
log.warn("Local#setIcon:"+progress);
return;
}
if(Preferences.instance().getBoolean("queue.download.updateIcon")) {
synchronized(lock) {
this.jni_load();
Expand All @@ -229,6 +238,9 @@ public void setProgress(int progress) {
NSWorkspace.sharedWorkspace().noteFileSystemChangedAtPath(this.getAbsolute());
}

/**
* Removes the resource fork from the file alltogether
*/
private void removeResourceFork() {
try {
this.removeCustomIcon();
Expand Down
127 changes: 66 additions & 61 deletions source/ch/cyberduck/core/Path.java
Expand Up @@ -27,6 +27,8 @@
import org.apache.log4j.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Iterator;
Expand Down Expand Up @@ -306,15 +308,15 @@ public static String normalize(final String path) {
normalized = normalized.substring(0, normalized.lastIndexOf('/', index - 1)) +
normalized.substring(index + 3);
}
// Resolve occurrences of "//" in the normalized path
while(true) {
int index = normalized.indexOf("//");
if(index < 0) {
break;
}
normalized = normalized.substring(0, index) +
normalized.substring(index + 1);
}
// // Resolve occurrences of "//" in the normalized path
// while(true) {
// int index = normalized.indexOf("//");
// if(index < 0) {
// break;
// }
// normalized = normalized.substring(0, index) +
// normalized.substring(index + 1);
// }
while(normalized.endsWith(DELIMITER) && normalized.length() > 1) {
//Strip any redundant delimiter at the end of the path
normalized = normalized.substring(0, normalized.length() - 1);
Expand Down Expand Up @@ -602,83 +604,86 @@ public boolean isSkipped() {
}

/**
* Will copy from in to out. Will attempt to skip Status#getCurrent
* from the inputstream but not from the outputstream. The outputstream
* is asssumed to append to a already existing file if
* Status#getCurrent > 0
* @param in The stream to read from
* @param out The stream to write to
* @throws IOResumeException If the input stream fails to skip the appropriate
* number of bytes
*/
public void upload(java.io.OutputStream out, java.io.InputStream in) throws IOException {
public void upload(OutputStream out, InputStream in) throws IOException {
if(log.isDebugEnabled()) {
log.debug("upload(" + out.toString() + ", " + in.toString());
}
this.getSession().message(NSBundle.localizedString("Uploading", "Status", "") + " " + this.getName());
if(this.status.isResume()) {
long skipped = in.skip(this.status.getCurrent());
if(status.isResume()) {
long skipped = in.skip(status.getCurrent());
log.info("Skipping " + skipped + " bytes");
if(skipped < this.status.getCurrent()) {
throw new IOException("Resume failed: Skipped " + skipped + " bytes instead of " + this.status.getCurrent());
}
}
if(log.isDebugEnabled()) {
log.debug("upload(" + in.toString() + ", " + out.toString());
}
int chunksize = Preferences.instance().getInteger("connection.buffer");
byte[] chunk = new byte[chunksize];
int amount = 0;
long current = this.status.getCurrent();
boolean complete = false;
// read from socket (bytes) & write to file in chunks
while(!complete && !status.isCanceled()) {
amount = in.read(chunk, 0, chunksize);
if(-1 == amount) {
complete = true;
}
else {
out.write(chunk, 0, amount);
this.status.setCurrent(current += amount);
out.flush();
if(skipped < status.getCurrent()) {
throw new IOResumeException("Skipped " + skipped + " bytes instead of " + status.getCurrent());
}
}
this.status.setComplete(complete);
this.transfer(in, out, new AbstractStreamListener());
}

/**
* Will copy from in to out. Does not attempt to skip any bytes from the streams.
* @param in The stream to read from
* @param out The stream to write to
*/
public void download(java.io.InputStream in, java.io.OutputStream out) throws IOException {
public void download(InputStream in, OutputStream out) throws IOException {
if(log.isDebugEnabled()) {
log.debug("download(" + in.toString() + ", " + out.toString());
}
this.getSession().message(NSBundle.localizedString("Downloading", "Status", "") + " " + this.getName());
int chunksize = Preferences.instance().getInteger("connection.buffer");
byte[] chunk = new byte[chunksize];
long current = this.status.getCurrent();
final boolean updateProgress = this.attributes.getSize() > Status.MEGA * 5;
int step = 0;
this.getLocal().setProgress(step);
// read from socket (bytes) & write to file in chunks
int amount = 0;
boolean complete = false;
while(!complete && !status.isCanceled()) {
amount = in.read(chunk, 0, chunksize);
if(-1 == amount) {
complete = true;
}
else {
out.write(chunk, 0, amount);
this.status.setCurrent(current += amount);
if(updateProgress) {
int fraction = (int) (status.getCurrent() / this.attributes.getSize() * 10);
// Only update the file custom icon if the size is > 5MB. Otherwise creating too much
// overhead when transferring a large amount of files
final boolean updateIcon = attributes.getSize() > Status.MEGA * 5;
// Set the first progress icon
this.getLocal().setIcon(0);
this.transfer(in, out, new AbstractStreamListener() {
int step = 0;

public void bytesReceived(int bytes) {
if(-1 == bytes) {
// Remove custom icon if complete. The Finder will display the default
// icon for this filetype
getLocal().setIcon(-1);
}
if(updateIcon) {
int fraction = (int) (status.getCurrent() / attributes.getSize() * 10);
// An integer between 0 and 9
if(fraction > step) {
this.getLocal().setProgress(++step);
// Another 10 percent of the file has been transferred
getLocal().setIcon(++step);
}
}
out.flush();
}
});
}

private void transfer(InputStream in, OutputStream out, StreamListener listener)
throws IOException
{
final int chunksize = Preferences.instance().getInteger("connection.buffer");
byte[] chunk = new byte[chunksize];
long bytesTransferred = status.getCurrent();
while(!status.isCanceled()) {
int read = in.read(chunk, 0, chunksize);
listener.bytesReceived(read);
if(-1 == read) {
// End of file
status.setComplete(true);
break;
}
out.write(chunk, 0, read);
listener.bytesSent(read);
bytesTransferred += read;
status.setCurrent(bytesTransferred);
}
if(complete) {
this.getLocal().setProgress(-1);
}
this.status.setComplete(complete);
out.flush();
}

/**
Expand Down
29 changes: 29 additions & 0 deletions source/ch/cyberduck/core/StreamListener.java
@@ -0,0 +1,29 @@
package ch.cyberduck.core;

/*
* Copyright (c) 2007 David Kocher. All rights reserved.
* http://cyberduck.ch/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* dkocher@cyberduck.ch
*/

/**
* @version $Id$
*/
public interface StreamListener
{
public void bytesSent(int bytes);

public void bytesReceived(int bytes);
}
11 changes: 1 addition & 10 deletions source/ch/cyberduck/core/ftp/FTPPath.java
Expand Up @@ -277,7 +277,7 @@ else if(this.attributes.isDirectory()) {
}
Path file = (Path) iter.next();
if(file.attributes.isFile()) {
session.message(NSBundle.localizedString("Deleting", "Status", "") + " " + this.getName());
session.message(NSBundle.localizedString("Deleting", "Status", "") + " " + file.getName());
session.FTP.delete(file.getName());
}
else if(file.attributes.isDirectory()) {
Expand Down Expand Up @@ -679,15 +679,6 @@ else if(Preferences.instance().getProperty("ftp.transfermode").equals(
try {
session.FTP.utime(this.getLocal().getModificationDate(),
this.getLocal().getCreationDate(), this.getName(), this.getHost().getTimezone());
// if(session.isUTIMESupported()) {
// session.FTP.utime(timestamp, this.getName(), this.getHost().getTimezone());
// }
// else if(session.isMDTMSetSupported()) {
// session.FTP.setmodtime(timestamp, this.getName(), this.getHost().getTimezone());
// }
// else {
// this.setTimestampFallback();
// }
}
catch(FTPException e) {
if(Preferences.instance().getBoolean("queue.upload.preserveDate.fallback")) {
Expand Down
4 changes: 2 additions & 2 deletions source/ch/cyberduck/core/sftp/SFTPPath.java
Expand Up @@ -428,7 +428,7 @@ public void download() {
long skipped = in.skip(this.status.getCurrent());
log.info("Skipping " + skipped + " bytes");
if(skipped < this.status.getCurrent()) {
throw new IOException("Resume failed: Skipped " + skipped + " bytes instead of " + this.status.getCurrent());
throw new IOResumeException("Skipped " + skipped + " bytes instead of " + this.status.getCurrent());
}
}
this.download(in, out);
Expand Down Expand Up @@ -561,7 +561,7 @@ public void upload() {
long skipped = out.skip(this.status.getCurrent());
log.info("Skipping " + skipped + " bytes");
if(skipped < this.status.getCurrent()) {
throw new IOException("Resume failed: Skipped " + skipped + " bytes instead of " + this.status.getCurrent());
throw new IOResumeException("Skipped " + skipped + " bytes instead of " + this.status.getCurrent());
}
}
this.upload(out, in);
Expand Down

0 comments on commit ceaf53f

Please sign in to comment.