boolean isAscii = session.getDataType() == DataType.ASCII;
long startTime = System.currentTimeMillis();
byte[] buff = new byte[4096];
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = IoUtils.getBufferedInputStream(in);
bos = IoUtils.getBufferedOutputStream(out);
DefaultFtpSession defaultFtpSession = null;
if (session instanceof DefaultFtpSession) {
defaultFtpSession = (DefaultFtpSession) session;
}
byte lastByte = 0;
while (true) {
// if current rate exceeds the max rate, sleep for 50ms
// and again check the current transfer rate
if (maxRate > 0) {
// prevent "divide by zero" exception
long interval = System.currentTimeMillis() - startTime;
if (interval == 0) {
interval = 1;
}
// check current rate
long currRate = (transferredSize * 1000L) / interval;
if (currRate > maxRate) {
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
break;
}
continue;
}
}
// read data
int count = bis.read(buff);
if (count == -1) {
break;
}
// update MINA session
if (defaultFtpSession != null) {
if (isWrite) {
defaultFtpSession.increaseWrittenDataBytes(count);
} else {
defaultFtpSession.increaseReadDataBytes(count);
}
}
// write data
// if ascii, replace \n by \r\n
if (isAscii) {
for (int i = 0; i < count; ++i) {
byte b = buff[i];
if(isWrite) {
if (b == '\n' && lastByte != '\r') {
bos.write('\r');
}
bos.write(b);
} else {
if(b == '\n') {
// for reads, we should always get \r\n
// so what we do here is to ignore \n bytes
// and on \r dump the system local line ending.
// Some clients won't transform new lines into \r\n so we make sure we don't delete new lines
if (lastByte != '\r'){
bos.write(EOL);
}
} else if(b == '\r') {
bos.write(EOL);
} else {
// not a line ending, just output
bos.write(b);
}
}
// store this byte so that we can compare it for line endings
lastByte = b;
}
} else {
bos.write(buff, 0, count);
}
transferredSize += count;
notifyObserver();
}
} catch(IOException e) {
LOG.warn("Exception during data transfer, closing data connection socket", e);
factory.closeDataConnection();
throw e;
} catch(RuntimeException e) {
LOG.warn("Exception during data transfer, closing data connection socket", e);
factory.closeDataConnection();
throw e;
} finally {
if (bos != null) {
bos.flush();
}
}
return transferredSize;
}