/**
*
* Copyright (C) 2007 Enterprise Distributed Technologies Ltd
*
* www.enterprisedt.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Bug fixes, suggestions and comments should be should posted on
* http://www.enterprisedt.com/forums/index.php
*
* Change Log:
*
* $Log: FTPOutputStream.java,v $
* Revision 1.2 2009-01-15 03:37:30 bruceb
* fix bug re this.remoteFile
*
* Revision 1.1 2007-12-18 07:52:06 bruceb
* 2.0 changes
*
*
*/
package com.enterprisedt.net.ftp;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.enterprisedt.util.debug.Logger;
/**
* Represents an output stream that writes to an FTP server, permitting
* the user to upload a file by writing to the stream. It can only be used
* for one upload, i.e. after the stream is closed it cannot be reopened.
*
* @author Bruce Blackshaw
* @version $Revision: 1.2 $
*/
public class FTPOutputStream extends FileTransferOutputStream {
private static Logger log = Logger.getLogger("FTPOutputStream");
/**
* Interval that we notify the monitor of progress
*/
private long monitorInterval;
/**
* The client being used to perform the transfer
*/
private FTPClient client;
/**
* The output stream to the FTP server
*/
private BufferedOutputStream out;
/**
* Number of bytes downloaded
*/
private long size = 0;
/**
* Is this an ASCII transfer or not?
*/
private boolean isASCII = false;
/**
* Count of byte since last the progress monitor was notified.
*/
private long monitorCount = 0;
/**
* Progress monitor reference
*/
private FTPProgressMonitor monitor;
/**
* Progress monitor reference
*/
private FTPProgressMonitorEx monitorEx;
/**
* Previously read bytes that match the line terminator
*/
private byte[] prevBuf = new byte[FTPClient.FTP_LINE_SEPARATOR.length];
/**
* Where we are up to in prevBuf re matches
*/
private int matchpos = 0;
/**
* Constructor. A connected FTPClient instance must be supplied. This sets up the
* download.
*
* @param client connected FTPClient instance (or subclass)
* @param remoteFile name of remote file (if null, the server <b>may</b>
* be able to generate a name
* @throws IOException
* @throws FTPException
*/
public FTPOutputStream(FTPClient client, String remoteFile) throws IOException, FTPException {
this(client, remoteFile, false);
}
/**
* Constructor. A connected FTPClient instance must be supplied. This sets up the
* download.
*
* @param client connected FTPClient instance (or subclass)
* @param remoteFile name of remote file (if null, the server <b>may</b>
* be able to generate a name
* @param append true if appending on the server
* @throws IOException
* @throws FTPException
*/
public FTPOutputStream(FTPClient client, String remoteFile, boolean append) throws IOException, FTPException {
this.client = client;
this.remoteFile = remoteFile;
try {
this.remoteFile = client.initPut(remoteFile, append);
// get an input stream to read data from ... AFTER we have
// the ok to go ahead AND AFTER we've successfully opened a
// stream for the local file
out =
new BufferedOutputStream(
new DataOutputStream(client.getOutputStream()), client.getTransferBufferSize()*2);
}
catch (IOException ex) {
client.validateTransferOnError(ex);
throw ex;
}
this.monitorInterval = client.getMonitorInterval();
this.monitor = client.getProgressMonitor();
this.isASCII = (client.getType().equals(FTPTransferType.ASCII));
}
/**
* The output stream uses the progress monitor currently owned by the FTP client.
* This method allows a different progress monitor to be passed in, or for the
* monitor interval to be altered.
*
* @param monitor progress monitor reference
* @param monitorInterval
*/
public void setMonitor(FTPProgressMonitorEx monitor, long monitorInterval) {
this.monitor = monitor;
this.monitorEx = monitor;
this.monitorInterval = monitorInterval;
}
/**
* Writes <code>b.length</code> bytes from the specified byte array
* to this output stream.
*
* @param b the data.
* @exception IOException if an I/O error occurs.
*/
public void write(int b) throws IOException {
byte[] tmp = new byte[1];
tmp[0] = (byte)b;
write(tmp, 0, 1);
}
/**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this output stream.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
* @exception IOException if an I/O error occurs.
*/
public void write(byte b[], int off, int len) throws IOException {
if (isASCII) {
for (int i = off; i < off+len; i++) {
// LF without preceding CR (i.e. Unix text file)
if (b[i] == FTPClient.LINE_FEED && matchpos == 0) {
out.write(FTPClient.CARRIAGE_RETURN);
out.write(FTPClient.LINE_FEED);
size += 2;
monitorCount += 2;
}
else if (b[i] == FTPClient.FTP_LINE_SEPARATOR[matchpos]) {
prevBuf[matchpos] = b[i];
matchpos++;
if (matchpos == FTPClient.FTP_LINE_SEPARATOR.length) {
out.write(FTPClient.CARRIAGE_RETURN);
out.write(FTPClient.LINE_FEED);
size += 2;
monitorCount += 2;
matchpos = 0;
}
}
else { // no match current char
// this must be a matching \r if we matched first char
if (matchpos > 0) {
out.write(FTPClient.CARRIAGE_RETURN);
out.write(FTPClient.LINE_FEED);
size += 2;
monitorCount += 2;
}
out.write(b[i]);
size++;
monitorCount++;
matchpos = 0;
}
}
}
else { // binary
out.write(b, off, len);
size += len;
monitorCount += len;
}
if (monitor != null && monitorCount > monitorInterval) {
monitor.bytesTransferred(size);
monitorCount = 0;
}
}
/**
* Closes this output stream and releases any system resources associated
* with the stream. This <b>must</b> be called before any other operations
* are initiated on the FTPClient.
*
* @exception IOException if an I/O error occurs.
*/
public void close() throws IOException {
if (!closed) {
closed = true;
// write out anything left at the end that has been saved
if (isASCII && matchpos > 0) {
out.write(prevBuf, 0, matchpos);
size += matchpos;
monitorCount += matchpos;
}
client.forceResumeOff();
// close streams
client.closeDataSocket(out);
if (monitor != null)
monitor.bytesTransferred(size);
// log bytes transferred
log.debug("Transferred " + size + " bytes from remote host");
try {
client.validateTransfer();
}
catch (FTPException ex) {
throw new IOException(ex.getMessage());
}
if (monitorEx != null)
monitorEx.transferComplete(TransferDirection.UPLOAD, remoteFile);
}
}
}