001: /**
002: *
003: * Copyright (C) 2007 Enterprise Distributed Technologies Ltd
004: *
005: * www.enterprisedt.com
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Bug fixes, suggestions and comments should be should posted on
022: * http://www.enterprisedt.com/forums/index.php
023: *
024: * Change Log:
025: *
026: * $Log: FTPOutputStream.java,v $
027: * Revision 1.1 2007-12-18 07:52:06 bruceb
028: * 2.0 changes
029: *
030: *
031: */package com.enterprisedt.net.ftp;
032:
033: import java.io.BufferedOutputStream;
034: import java.io.DataOutputStream;
035: import java.io.IOException;
036:
037: import com.enterprisedt.util.debug.Logger;
038:
039: /**
040: * Represents an output stream that writes to an FTP server, permitting
041: * the user to upload a file by writing to the stream. It can only be used
042: * for one upload, i.e. after the stream is closed it cannot be reopened.
043: *
044: * @author Bruce Blackshaw
045: * @version $Revision: 1.1 $
046: */
047: public class FTPOutputStream extends FileTransferOutputStream {
048:
049: private static Logger log = Logger.getLogger("FTPOutputStream");
050:
051: /**
052: * Interval that we notify the monitor of progress
053: */
054: private long monitorInterval;
055:
056: /**
057: * The client being used to perform the transfer
058: */
059: private FTPClient client;
060:
061: /**
062: * The output stream to the FTP server
063: */
064: private BufferedOutputStream out;
065:
066: /**
067: * Number of bytes downloaded
068: */
069: private long size = 0;
070:
071: /**
072: * Is this an ASCII transfer or not?
073: */
074: private boolean isASCII = false;
075:
076: /**
077: * Count of byte since last the progress monitor was notified.
078: */
079: private long monitorCount = 0;
080:
081: /**
082: * Progress monitor reference
083: */
084: private FTPProgressMonitor monitor;
085:
086: /**
087: * Progress monitor reference
088: */
089: private FTPProgressMonitorEx monitorEx;
090:
091: /**
092: * Previously read bytes that match the line terminator
093: */
094: private byte[] prevBuf = new byte[FTPClient.FTP_LINE_SEPARATOR.length];
095:
096: /**
097: * Where we are up to in prevBuf re matches
098: */
099: private int matchpos = 0;
100:
101: /**
102: * Constructor. A connected FTPClient instance must be supplied. This sets up the
103: * download.
104: *
105: * @param client connected FTPClient instance (or subclass)
106: * @param remoteFile name of remote file (if null, the server <b>may</b>
107: * be able to generate a name
108: * @throws IOException
109: * @throws FTPException
110: */
111: public FTPOutputStream(FTPClient client, String remoteFile)
112: throws IOException, FTPException {
113: this (client, remoteFile, false);
114: }
115:
116: /**
117: * Constructor. A connected FTPClient instance must be supplied. This sets up the
118: * download.
119: *
120: * @param client connected FTPClient instance (or subclass)
121: * @param remoteFile name of remote file (if null, the server <b>may</b>
122: * be able to generate a name
123: * @param append true if appending on the server
124: * @throws IOException
125: * @throws FTPException
126: */
127: public FTPOutputStream(FTPClient client, String remoteFile,
128: boolean append) throws IOException, FTPException {
129: this .client = client;
130: this .remoteFile = remoteFile;
131: try {
132: remoteFile = client.initPut(remoteFile, append);
133:
134: // get an input stream to read data from ... AFTER we have
135: // the ok to go ahead AND AFTER we've successfully opened a
136: // stream for the local file
137: out = new BufferedOutputStream(new DataOutputStream(client
138: .getOutputStream()),
139: client.getTransferBufferSize() * 2);
140:
141: } catch (IOException ex) {
142: client.validateTransferOnError(ex);
143: throw ex;
144: }
145:
146: this .monitorInterval = client.getMonitorInterval();
147: this .monitor = client.getProgressMonitor();
148: this .isASCII = (client.getType().equals(FTPTransferType.ASCII));
149: }
150:
151: /**
152: * The output stream uses the progress monitor currently owned by the FTP client.
153: * This method allows a different progress monitor to be passed in, or for the
154: * monitor interval to be altered.
155: *
156: * @param monitor progress monitor reference
157: * @param monitorInterval
158: */
159: public void setMonitor(FTPProgressMonitorEx monitor,
160: long monitorInterval) {
161: this .monitor = monitor;
162: this .monitorEx = monitor;
163: this .monitorInterval = monitorInterval;
164: }
165:
166: /**
167: * Writes <code>b.length</code> bytes from the specified byte array
168: * to this output stream.
169: *
170: * @param b the data.
171: * @exception IOException if an I/O error occurs.
172: */
173: public void write(int b) throws IOException {
174: byte[] tmp = new byte[1];
175: tmp[0] = (byte) b;
176: write(tmp, 0, 1);
177: }
178:
179: /**
180: * Writes <code>len</code> bytes from the specified byte array
181: * starting at offset <code>off</code> to this output stream.
182: *
183: * @param b the data.
184: * @param off the start offset in the data.
185: * @param len the number of bytes to write.
186: * @exception IOException if an I/O error occurs.
187: */
188: public void write(byte b[], int off, int len) throws IOException {
189: if (isASCII) {
190: for (int i = off; i < off + len; i++) {
191: // LF without preceding CR (i.e. Unix text file)
192: if (b[i] == FTPClient.LINE_FEED && matchpos == 0) {
193: out.write(FTPClient.CARRIAGE_RETURN);
194: out.write(FTPClient.LINE_FEED);
195: size += 2;
196: monitorCount += 2;
197: } else if (b[i] == FTPClient.FTP_LINE_SEPARATOR[matchpos]) {
198: prevBuf[matchpos] = b[i];
199: matchpos++;
200: if (matchpos == FTPClient.FTP_LINE_SEPARATOR.length) {
201: out.write(FTPClient.CARRIAGE_RETURN);
202: out.write(FTPClient.LINE_FEED);
203: size += 2;
204: monitorCount += 2;
205: matchpos = 0;
206: }
207: } else { // no match current char
208: // this must be a matching \r if we matched first char
209: if (matchpos > 0) {
210: out.write(FTPClient.CARRIAGE_RETURN);
211: out.write(FTPClient.LINE_FEED);
212: size += 2;
213: monitorCount += 2;
214: }
215: out.write(b[i]);
216: size++;
217: monitorCount++;
218: matchpos = 0;
219: }
220: }
221: } else { // binary
222: out.write(b, off, len);
223: size += len;
224: monitorCount += len;
225: }
226:
227: if (monitor != null && monitorCount > monitorInterval) {
228: monitor.bytesTransferred(size);
229: monitorCount = 0;
230: }
231: }
232:
233: /**
234: * Closes this output stream and releases any system resources associated
235: * with the stream. This <b>must</b> be called before any other operations
236: * are initiated on the FTPClient.
237: *
238: * @exception IOException if an I/O error occurs.
239: */
240: public void close() throws IOException {
241:
242: if (!closed) {
243: closed = true;
244:
245: // write out anything left at the end that has been saved
246: if (isASCII && matchpos > 0) {
247: out.write(prevBuf, 0, matchpos);
248: size += matchpos;
249: monitorCount += matchpos;
250: }
251:
252: client.forceResumeOff();
253:
254: // close streams
255: client.closeDataSocket(out);
256:
257: if (monitor != null)
258: monitor.bytesTransferred(size);
259:
260: // log bytes transferred
261: log
262: .debug("Transferred " + size
263: + " bytes from remote host");
264:
265: try {
266: client.validateTransfer();
267: } catch (FTPException ex) {
268: throw new IOException(ex.getMessage());
269: }
270:
271: if (monitorEx != null)
272: monitorEx.transferComplete(TransferDirection.UPLOAD,
273: remoteFile);
274: }
275: }
276:
277: }
|