source: 3thparty/jupload/src/main/java/wjhk/jupload2/upload/FileUploadThreadFTP.java @ 3951

Revision 3951, 21.1 KB checked in by alexandrecorreia, 13 years ago (diff)

Ticket #1709 - Adicao de codigo fonte java do componente jupload

Line 
1//
2// $Id: FileUploadThreadFTP.java 136 2007-05-12 20:15:36 +0000 (sam., 12 mai
3// 2007) felfert $
4//
5// jupload - A file upload applet.
6// Copyright 2007 The JUpload Team
7//
8// Created: 2007-01-01
9// Creator: etienne_sf
10// Last modified: $Date: 2010-02-26 07:45:47 -0300 (Sex, 26 Fev 2010) $
11//
12// This program is free software; you can redistribute it and/or modify it under
13// the terms of the GNU General Public License as published by the Free Software
14// Foundation; either version 2 of the License, or (at your option) any later
15// version. This program is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18// details. You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software Foundation, Inc.,
20// 675 Mass Ave, Cambridge, MA 02139, USA.
21
22package wjhk.jupload2.upload;
23
24import java.io.BufferedOutputStream;
25import java.io.IOException;
26import java.io.OutputStream;
27import java.util.Iterator;
28import java.util.SortedSet;
29import java.util.StringTokenizer;
30import java.util.TreeSet;
31import java.util.concurrent.BlockingQueue;
32import java.util.regex.Matcher;
33import java.util.regex.Pattern;
34
35import org.apache.commons.net.ftp.FTP;
36import org.apache.commons.net.ftp.FTPClient;
37import org.apache.commons.net.ftp.FTPConnectionClosedException;
38import org.apache.commons.net.ftp.FTPReply;
39
40import wjhk.jupload2.exception.JUploadException;
41import wjhk.jupload2.exception.JUploadExceptionUploadFailed;
42import wjhk.jupload2.exception.JUploadIOException;
43import wjhk.jupload2.policies.UploadPolicy;
44
45/**
46 * The FileUploadThreadFTP class is intended to extend the functionality of the
47 * JUpload applet and allow it to handle ftp:// addresses. <br>
48 * Note: this class is not a V4 of the FTP upload. It is named V4, as it
49 * inherits from the {@link FileUploadThread} class. <br>
50 * <br>
51 * In order to use it, simply change the postURL argument to the applet to
52 * contain the appropriate ftp:// link. The format is:
53 *
54 * <pre>
55 *         ftp://username:password@myhost.com:21/directory
56 * </pre>
57 *
58 * Where everything but the host is optional. There is another parameter that
59 * can be passed to the applet named 'binary' which will set the file transfer
60 * mode based on the value. The possible values here are 'true' or 'false'. It
61 * was intended to be somewhat intelligent by looking at the file extension and
62 * basing the transfer mode on that, however, it was never implemented. Feel
63 * free to! Also, there is a 'passive' parameter which also has a value of
64 * 'true' or 'false' which sets the connection type to either active or passive
65 * mode.
66 *
67 * @author Evin Callahan (inheritance from DefaultUploadThread built by
68 *         etienne_sf)
69 * @author Daystar Computer Services
70 * @see FileUploadThread
71 * @see DefaultFileUploadThread
72 * @version 1.0, 01 Jan 2007 * Update march 2007, etienne_sf Adaptation to match
73 *          all JUpload functions: <DIR> <LI>Inheritance from the
74 *          {@link FileUploadThread} class, <LI>Use of the UploadFileData class,
75 *          <LI>Before upload file preparation, <LI>Upload stop by the user. <LI>
76 *          </DIR>
77 */
78public class FileUploadThreadFTP extends DefaultFileUploadThread {
79
80    // ////////////////////////////////////////////////////////////////////////////////////
81    // /////////////////////// PRIVATE ATTRIBUTES
82    // ///////////////////////////////////////
83    // ////////////////////////////////////////////////////////////////////////////////////
84
85    // ////////////////////////////////////////////////////////////////////////////////////
86    // /////////////////////// PRIVATE ATTRIBUTES
87    // ///////////////////////////////////////
88    // ////////////////////////////////////////////////////////////////////////////////////
89
90    /**
91     * The output stream, where the current file should be written. This output
92     * stream should not be used. The buffered one is much faster.
93     */
94    private OutputStream ftpOutputStream = null;
95
96    /**
97     * The buffered stream, that the application should use for upload.
98     */
99    private BufferedOutputStream bufferedOutputStream = null;
100
101    private Matcher uriMatch;
102
103    // the client that does the actual connecting to the server
104    private FTPClient ftp = new FTPClient();
105
106    /** FTP user, taken from the postURL applet parameter */
107    private String user;
108
109    /** FTP password, taken from the postURL applet parameter */
110    private String pass;
111
112    /** FTP target host, taken from the postURL applet parameter */
113    private String host;
114
115    /** FTP target port, taken from the postURL applet parameter */
116    private String port;
117
118    /**
119     * FTP target root folder for the upload, taken from the postURL applet
120     * parameter
121     */
122    private String ftpRootFolder;
123
124    /**
125     * Indicates whether the connection to the FTP server is open or not. This
126     * allows to connect once on the FTP server, for multiple file upload.
127     */
128    private boolean bConnected = false;
129
130    /**
131     * This pattern defines the groups and pattern of the ftp syntax.
132     */
133    public final Pattern ftpPattern = Pattern
134            .compile("^ftp://(([^:]+):([^\\@]+)\\@)?([^/:]+):?([0-9]+)?(/(.*))?$");
135
136    /**
137     * Creates a new instance. Performs the connection to the server based on
138     * the matcher created in the main.
139     *
140     * @param uploadPolicy
141     * @param packetQueue The queue from wich packets to upload are available.
142     * @param fileUploadManagerThread
143     * @throws JUploadException
144     * @throws IllegalArgumentException if any error occurs. message is error
145     */
146    public FileUploadThreadFTP(UploadPolicy uploadPolicy,
147            BlockingQueue<UploadFilePacket> packetQueue,
148            FileUploadManagerThread fileUploadManagerThread)
149            throws JUploadException {
150        super("FileUploadThreadFTP thread", packetQueue, uploadPolicy,
151                fileUploadManagerThread);
152        this.uploadPolicy.displayDebug("[FileUploadThreadFTP]  Using "
153                + this.getClass().getName(), 30);
154
155        // Some coherence checks, for parameter given to the applet.
156
157        // stringUploadSuccess: unused in FTP mode. Must be null.
158        if (uploadPolicy.getStringUploadSuccess() != null) {
159            uploadPolicy
160                    .displayWarn("FTP mode: stringUploadSuccess parameter ignored (forced to null)");
161            uploadPolicy.setProperty(UploadPolicy.PROP_STRING_UPLOAD_SUCCESS,
162                    null);
163        }
164
165        // nbFilesPerRequest: must be 1 in FTP mode.
166        if (uploadPolicy.getNbFilesPerRequest() != 1) {
167            uploadPolicy
168                    .displayWarn("FTP mode: nbFilesPerRequest parameter ignored (forced to 1)");
169            uploadPolicy.setProperty(UploadPolicy.PROP_NB_FILES_PER_REQUEST,
170                    "1");
171        }
172
173        // maxChunkSize: must be unlimited (no chunk management in FTP mode).
174        if (uploadPolicy.getMaxChunkSize() != Long.MAX_VALUE) {
175            uploadPolicy
176                    .displayWarn("FTP mode: maxChunkSize parameter ignored (forced to Long.MAX_VALUE)");
177            uploadPolicy.setProperty(UploadPolicy.PROP_MAX_CHUNK_SIZE, Long
178                    .toString(Long.MAX_VALUE));
179        }
180    }
181
182    /** @see DefaultFileUploadThread#beforeRequest(UploadFilePacket) */
183    @Override
184    void beforeRequest(UploadFilePacket packet) throws JUploadException {
185
186        // If we're connected, we need to check the connection.
187        if (this.bConnected) {
188            // Let's check the connection is still Ok.
189            try {
190                this.ftp.sendNoOp();
191            } catch (FTPConnectionClosedException eClosed) {
192                // Let's forget this connection.
193                this.bConnected = false;
194            } catch (IOException e) {
195                throw new JUploadIOException(e.getClass().getName()
196                        + " while checking FTP connection to the server", e);
197            }
198        }
199
200        // If not already connected ... we connect to the server.
201        if (!this.bConnected) {
202            // Let's connect to the FTP server.
203            String url = this.uploadPolicy.getPostURL();
204            this.uriMatch = this.ftpPattern.matcher(url);
205            if (!this.uriMatch.matches()) {
206                throw new JUploadException("invalid URI: " + url);
207            }
208            this.user = this.uriMatch.group(2) == null ? "anonymous"
209                    : this.uriMatch.group(2);
210            this.pass = this.uriMatch.group(3) == null ? "JUpload"
211                    : this.uriMatch.group(3);
212            this.host = this.uriMatch.group(4); // no default server
213            this.port = this.uriMatch.group(5) == null ? "21" : this.uriMatch
214                    .group(5);
215            this.ftpRootFolder = (this.uriMatch.group(7) == null) ? null : "/"
216                    + this.uriMatch.group(7);
217            // The last character must be a slash
218            if (this.ftpRootFolder != null && !this.ftpRootFolder.endsWith("/")) {
219                this.ftpRootFolder += "/";
220            }
221
222            // do connect.. any error will be thrown up the chain
223            try {
224                this.ftp.setDefaultPort(Integer.parseInt(this.port));
225                this.ftp.connect(this.host);
226                this.uploadPolicy.displayDebug(
227                        "[FileUploadThreadFTP] Connected to " + this.host, 10);
228                this.uploadPolicy.displayDebug(this.ftp.getReplyString(), 80);
229
230                if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode()))
231                    throw new JUploadException("FTP server refused connection.");
232
233                // given the login information, do the login
234                this.ftp.login(this.user, this.pass);
235                this.uploadPolicy.displayDebug("[FileUploadThreadFTP] "
236                        + this.ftp.getReplyString(), 80);
237
238                if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode()))
239                    throw new JUploadException(
240                            "Invalid ftp username / password");
241
242                this.bConnected = true;
243            } catch (JUploadException jue) {
244                // No special action, we keep the exception untouched
245                throw jue;
246            } catch (IOException ioe) {
247                throw new JUploadIOException(ioe.getClass().getName()
248                        + "[FTP] Could not connect to server ("
249                        + ioe.getMessage() + ")", ioe);
250            } catch (Exception e) {
251                throw new JUploadException(e.getClass().getName()
252                        + "[FTP] Could not connect to server ("
253                        + e.getMessage() + ")", e);
254            }
255
256            // now do the same for the passive/active parameter
257            if (this.uploadPolicy.getFtpTransfertPassive()) {
258                this.ftp.enterLocalPassiveMode();
259            } else {
260                this.ftp.enterLocalActiveMode();
261            }
262
263        } // if(!bConnected)
264    }
265
266    /** @see DefaultFileUploadThread#afterFile(UploadFileData) */
267    @Override
268    void afterFile(UploadFileData uploadFileData) {
269        // Nothing to do
270    }
271
272    /** @see DefaultFileUploadThread#beforeFile(UploadFilePacket, UploadFileData) */
273    @Override
274    void beforeFile(UploadFilePacket uploadFilePacket,
275            UploadFileData uploadFileData) throws JUploadException {
276        String workingDir = null;
277        try {
278            // if configured to, we go to the relative sub-folder of the current
279            // file, or on the root of the postURL.
280            if (this.uploadPolicy.getFtpCreateDirectoryStructure()) {
281                // We create the FTP directory structure
282                // TODO: call it once for all files, not once for each file.
283                createDirectoryStructure(uploadFilePacket);
284
285                workingDir = this.ftpRootFolder
286                        + uploadFileData.getRelativeDir();
287                // We want to have only slashes, as anti-slashes may generate
288                // FTP errors.
289                workingDir = workingDir.replace("\\", "/");
290
291                this.uploadPolicy
292                        .displayDebug(
293                                "[FileUploadThreadFTP] ftpCreateDirectoryStructure: Changing working directory to: "
294                                        + workingDir, 80);
295            } else {
296                workingDir = this.ftpRootFolder;
297            }
298
299            if (workingDir != null && !workingDir.equals("")
300                    && !workingDir.equals(".")) {
301                this.ftp.changeWorkingDirectory(workingDir);
302                this.uploadPolicy.displayDebug("[FileUploadThreadFTP] "
303                        + this.ftp.getReplyString(), 80);
304            }
305
306            if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
307                throw new JUploadException(
308                        "[FTP] Error while changing directory to: "
309                                + workingDir + " (" + this.ftp.getReplyString()
310                                + ")");
311            }
312
313            setTransferType(uploadFileData);
314
315            // No delete, as the user may not have the right for that. We use,
316            // later, the store command:
317            // If the file already exists, it will be replaced.
318            // ftp.deleteFile(filesToUpload[index].getFileName());
319
320            // Let's open the stream for this file.
321            this.ftpOutputStream = this.ftp.storeFileStream(uploadFileData
322                    .getFileName());
323            if (this.ftpOutputStream == null) {
324                throw new JUploadIOException(
325                        "Stream connection to the server error. Check that your path on the URL is valid. postURL used is: "
326                                + this.uploadPolicy.getPostURL());
327            }
328            // The upload is done through a BufferedOutputStream. This speed up
329            // the upload in an unbelievable way ...
330            this.bufferedOutputStream = new BufferedOutputStream(
331                    this.ftpOutputStream);
332        } catch (IOException e) {
333            throw new JUploadIOException(e);
334        }
335    }
336
337    /** @see DefaultFileUploadThread#cleanAll() */
338    @Override
339    void cleanAll() {
340        try {
341            if (this.ftp.isConnected()) {
342                this.ftp.disconnect();
343                this.uploadPolicy.displayDebug(
344                        "[FileUploadThreadFTP] disconnected", 50);
345            }
346        } catch (IOException e) {
347            // then we arent connected
348            this.uploadPolicy.displayDebug(
349                    "[FileUploadThreadFTP] Not connected", 50);
350        } finally {
351            this.ftpOutputStream = null;
352            this.bufferedOutputStream = null;
353        }
354    }
355
356    /** @see DefaultFileUploadThread#cleanRequest() */
357    @Override
358    void cleanRequest() throws JUploadException {
359        if (this.bufferedOutputStream != null) {
360            try {
361                this.bufferedOutputStream.close();
362                this.ftpOutputStream.close();
363                if (!this.ftp.completePendingCommand()) {
364                    throw new JUploadExceptionUploadFailed(
365                            "ftp.completePendingCommand() returned false");
366                }
367            } catch (IOException e) {
368                throw new JUploadException(e);
369            } finally {
370                this.bufferedOutputStream = null;
371            }
372        }
373    }
374
375    /**
376     * @throws JUploadIOException
377     * @see DefaultFileUploadThread#finishRequest()
378     */
379    @Override
380    int finishRequest() throws JUploadException {
381        try {
382            getOutputStream().flush();
383            return 200;
384        } catch (IOException ioe) {
385            throw new JUploadIOException("FileUploadThreadFTP.finishRequest()",
386                    ioe);
387        } catch (Exception e) {
388            // When the user may not override an existing file, I got a
389            // NullPointerException. Let's trap all errors here.
390            throw new JUploadException(
391                    "FileUploadThreadFTP.finishRequest()  (check the user permission on the server)",
392                    e);
393        }
394    }
395
396    /** @see DefaultFileUploadThread#getAdditionnalBytesForUpload(UploadFileData) */
397    @Override
398    long getAdditionnalBytesForUpload(UploadFileData uploadFileData) {
399        // Default: no additional byte.
400        return 0;
401    }
402
403    /** @see DefaultFileUploadThread#getOutputStream() */
404    @Override
405    OutputStream getOutputStream() {
406        return this.bufferedOutputStream;
407    }
408
409    /** @see DefaultFileUploadThread#startRequest(long, boolean, int, boolean) */
410    @Override
411    void startRequest(long contentLength, boolean bChunkEnabled, int chunkPart,
412            boolean bLastChunk) {
413        // Nothing to do
414    }
415
416    /**
417     * Will set the binary/ascii value based on the parameters to the applet.
418     * This could be done by file extension too but it is not implemented.
419     *
420     * @param uploadFileData The file that we want to upload.
421     * @throws IOException if an error occurs while setting mode data
422     */
423    private void setTransferType(UploadFileData uploadFileData)
424            throws JUploadIOException {
425        try {
426            // read the value given from the user
427            if (this.uploadPolicy.getFtpTransfertBinary()) {
428                this.ftp.setFileType(FTP.BINARY_FILE_TYPE);
429            } else {
430                this.ftp.setFileType(FTP.ASCII_FILE_TYPE);
431            }
432        } catch (IOException ioe) {
433            throw new JUploadIOException(
434                    "Cannot set transfert binary or ascii mode (binary: "
435                            + this.uploadPolicy.getFtpTransfertBinary() + ")",
436                    ioe);
437        }
438    }
439
440    /**
441     * Create all relative sub-directories, so the structure on the server
442     * reflects the structure of the uploaded files.
443     *
444     * @throws JUploadIOException When an error occurs during folder creation
445     */
446    // A tester
447    private void createDirectoryStructure(UploadFilePacket packet)
448            throws JUploadIOException {
449        SortedSet<String> foldersToCreate = new TreeSet<String>();
450        String folderName;
451        String intermediateFolderName;
452        StringTokenizer st;
453
454        // 1) Let's find all folders and sub-folders we'll have to create.
455        for (UploadFileData uploadFileData : packet) {
456            if (isInterrupted()) {
457                break;
458            }
459            folderName = uploadFileData.getRelativeDir();
460            folderName = folderName.replaceAll("\\\\", "/");
461            // Do we already have this folder ?
462            if (!foldersToCreate.contains(folderName)) {
463                // We add this folder, and all missing intermediate ones
464                st = new StringTokenizer(folderName, "/");
465                intermediateFolderName = this.ftpRootFolder;
466                while (st.hasMoreTokens()) {
467                    intermediateFolderName += st.nextToken() + "/";
468                    if (!foldersToCreate.contains(intermediateFolderName)) {
469                        this.uploadPolicy.displayDebug(
470                                "FTP structure identification: Adding subfolder "
471                                        + intermediateFolderName, 80);
472                        foldersToCreate.add(intermediateFolderName);
473                    }
474                }
475            }
476        }
477
478        // 2) Let's create theses folders.
479        try {
480            String folder;
481            for (Iterator<String> it = foldersToCreate.iterator(); it.hasNext();) {
482                folder = it.next();
483
484                // The folder is in the list of folder to create, created from
485                // the file list.
486                // We first check if the folder already exist.
487                this.ftp.changeWorkingDirectory(folder);
488                if (FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
489                    this.uploadPolicy.displayDebug(
490                            "[FileUploadThreadFTP] Folder " + folder
491                                    + " already exist", 80);
492                } else {
493                    // We can not guess if it's because the folder
494                    // doesn't exist, or if it's a 'real' error.
495                    // Let's try to create the folder.
496                    this.ftp.mkd(folder);
497                    this.uploadPolicy.displayDebug(
498                            "[FileUploadThreadFTP] Folder " + folder
499                                    + " created", 80);
500                    if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
501                        throw new JUploadIOException(
502                                "Error while creating folder '"
503                                        + folder
504                                        + "' ("
505                                        + this.ftp.getReplyString().replaceAll(
506                                                "\r\n", "") + ")");
507                    }
508                }
509            }
510        } catch (IOException ioe) {
511            throw new JUploadIOException(ioe.getClass().getName()
512                    + " in FileUploadThreadFTP.createDirectoryStructure()", ioe);
513        }
514    }
515
516    /** {@inheritDoc} */
517    void interruptionReceived() {
518        cleanAll();
519    }
520}
Note: See TracBrowser for help on using the repository browser.