//
// $Id: DefaultFileUploadThread.java 287 2007-06-17 09:07:04 +0000 (dim., 17
// juin 2007) felfert $
//
// jupload - A file upload applet.
// Copyright 2007 The JUpload Team
//
// Created: ?
// Creator: William JinHua Kwong
// Last modified: $Date: 2008-04-16 00:58:02 -0700 (Wed, 16 Apr 2008) $
//
// 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. You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation, Inc.,
// 675 Mass Ave, Cambridge, MA 02139, USA.
package wjhk.jupload2.upload;
import java.io.OutputStream;
import java.util.Date;
import javax.swing.JProgressBar;
import wjhk.jupload2.exception.JUploadException;
import wjhk.jupload2.exception.JUploadExceptionUploadFailed;
import wjhk.jupload2.exception.JUploadIOException;
import wjhk.jupload2.filedata.FileData;
import wjhk.jupload2.policies.UploadPolicy;
/**
* This class is based on the {@link FileUploadThread} class. It's an abstract
* class that contains the default implementation for the
* {@link FileUploadThread} interface. <BR>
* It contains the following abstract methods, which must be implemented in the
* children classes. These methods are called in this order: <DIR>
* <LI>For each upload request (for instance, upload of 3 files with
* nbFilesPerRequest to 2, makes 2 request: 2 files, then the last one): <DIR>
* <LI><I>try</I>
* <LI>{@link #startRequest}: start of the UploadRequest.
* <LI>Then, for each file to upload (according to the nbFilesPerRequest and
* maxChunkSize applet parameters) <DIR>
* <LI>beforeFile(int) is called before writting the bytes for this
* file (or this chunk)
* <LI>afterFile(int) is called after writting the bytes for this file
* (or this chunk) </DIR>
* <LI>finishRequest() </DIR> </LI>
* <I>finally</I>cleanRequest()
* <LI>Call of cleanAll(), to clean up any used resources, common to
* the whole upload. </DIR>
*/
public abstract class DefaultFileUploadThread extends Thread implements
FileUploadThread {
// ////////////////////////////////////////////////////////////////////////////////////
// /////////////////////// VARIABLES ///////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////
/*
* etienne_sf: this parameter is now removed. The incoming list of
* files to upload is now only managed in the constructor: it's up to it to
* manage files that can't be read. /** The given array containing the files
* to upload. Stored in the constructor, and used in the run() method.
*/
// FileData[] filesDataParam = null;
/**
* This array will contain a 'copy' of the relevant element of the
* filesDataParam array (see the constructor). After filling the
* filesToUpload array, the UploadThread will only manipulate it: all access
* to the file contained by the filesDataParam given to the constructor will
* be done through the {@link UploadFileData} contained by this array.
*/
UploadFileData[] filesToUpload = null;
/**
* The upload policy contains all parameters needed to define the way files
* should be uploaded, including the URL.
*/
UploadPolicy uploadPolicy = null;
/**
* The value of the applet parameter maxChunkSize, or its default value.
*/
// TODO to be moved to HTTP ????
long maxChunkSize;
/**
* Maximum number of files for FTP upload.
*/
int nbMaxFilesPerUpload;
/**
* If set to 'true', the thread will stop the crrent upload. This attribute
* is not private as the {@link UploadFileData} class use it.
*
* @see UploadFileData#uploadFile(java.io.OutputStream, long)
*/
boolean stop = false;
/* the last type we reported uploadProgress, what was the byte count that we reported? */
long bytesReported = 0;
long msecsReported = 0;
int nbSuccessfulUploads = 0;
int nbCancelledUploads = 0;
int nbErrorUploads=0;
public int[] getUploadStats(){
int[] a4 = new int[4];
a4[0] = (this.stop ? 0:1);
a4[1] = nbSuccessfulUploads;
a4[2] = nbCancelledUploads;
a4[3] = nbErrorUploads;
return a4;
}
FileData currentFileData;
/**
* Thread Exception, if any occured during upload.
*/
Exception uploadException = null;
// ////////////////////////////////////////////////////////////////////////////////////
// /////////////////////// PRIVATE ATTRIBUTES
// ///////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////
/**
* The progressBar bar, that will indicate to the user the upload state (0
* to 100%).
*/
private JProgressBar progressBar = null;
/**
* The total number of bytes to be sent. This allows the calculation of the
* progressBar bar
*/
private long totalFilesLength = 0;
/** Current number of bytes that have been uploaded. */
private long uploadedLength = 0;
private long startTime;
/**
* The response message from the server, if any
*/
private String responseMsg;
/**
* Creates a new instance.
*
* @param filesDataParam The files to be uploaded.
* @param uploadPolicy The upload policy to be applied.
* @param progressBar The progressBar bar to be updated.
*/
public DefaultFileUploadThread(FileData[] filesDataParam,
UploadPolicy uploadPolicy, JProgressBar progressBar) {
this.uploadPolicy = uploadPolicy;
this.progressBar = progressBar;
// ////////////////////////////////////////////////////////////////////////////
// Let's read the up-to-date upload parameters
// First: how many files can be read. The files that can't be read won't
// be uploaded.
int nbFilesToUpload = 0;
for (int i = 0; i < filesDataParam.length; i += 1) {
if (filesDataParam[i].canRead()) {
nbFilesToUpload += 1;
}
}
this.filesToUpload = new UploadFileData[nbFilesToUpload];
// Patch to manage files that can't be read: we don't want them in
// filesToUpload!
// So let's add to the filesToUpload array, only the files to upload.
// The use of a Vector instead of an array for filesToUpload would be
// easier here. But all the implementation of the whole upload code is
// based on filesToUpload being an array.
// TODO display a proper message to the user, when a file can't be read.
// Currently, a warning is written in the applet output. But it won't be
// visible if the log window is hidden. In this case, the user must see
// the 'readable' column in the applet, and see that it's not checked.
// Not really intuitive!
int iFileIndex = 0;
for (int i = 0; i < filesDataParam.length; i += 1) {
if (filesDataParam[i].canRead()) {
this.filesToUpload[iFileIndex++] = new UploadFileData(
filesDataParam[i], this, uploadPolicy);
} else {
uploadPolicy.displayWarn(filesDataParam[i].getFileName()
+ " is read only: it won't be uploaded.");
}
}
// We don't store any additional parameters here: their value can be
// updated by the real class that'll be used. See FileUploadThreadFTP
// constructor of instance.
// Upload parameters are read in the run() method, below.
}
/**
* @see wjhk.jupload2.upload.FileUploadThread#getUploadedLength()
*/
public long getUploadedLength() {
return this.uploadedLength;
}
/**
* @see wjhk.jupload2.upload.FileUploadThread#getTotalLength()
*/
public long getTotalLength() {
return this.totalFilesLength;
}
/** @see FileUploadThread#stopUpload() */
public void stopUpload() {
this.stop = true;
}
/** @see FileUploadThread#isUploadStopped() */
public boolean isUploadStopped() {
return this.stop;
}
/**
* Get the server Output.
*
* @return The StringBuffer that contains the full server HTTP response.
*/
public String getResponseMsg() {
return this.responseMsg;
}
/**
* Get the exception that occurs during upload.
*
* @return The exception, or null if no exception were thrown.
*/
public Exception getException() {
return this.uploadException;
}
/**
* Used by the UploadFileData.uploadFile(java.io.OutputStream, long)
* for each uploaded buffer
*
* @see wjhk.jupload2.upload.FileUploadThread#nbBytesUploaded(long)
*/
public void nbBytesUploaded(long nbBytes) {
this.uploadedLength += nbBytes;
}
/**
* Used by the UploadFileData.uploadFile(java.io.OutputStream, long)
* for each uploaded FILE...
*
* @see wjhk.jupload2.upload.FileUploadThread#nbBytesUploaded(long)
*/
public void nbFileBytesUploaded(long nbBytes) {
long now = (new Date()).getTime();
//this.uploadPolicy.displayDebug(
// "nbFileBytesUploaded: time difference "+(now-msecsReported), 70);
if (bytesReported==0 || ((now-msecsReported) > 800)) { // only update every XXX seconds, otherwise callback traffic too much
try{
this.currentFileData.uploadProgress(nbBytes,this.currentFileData.getUploadLength());
} catch (JUploadException ex) {
/* do nothing */
}
msecsReported = now;
bytesReported = nbBytes;
}
}
/**
* This method is called before the upload. It calls the
* {@link FileData#beforeUpload()} method for all files to upload, and
* prepares the progressBar bar (if any), with total number of bytes to
* upload.
*/
final private void beforeUpload() throws JUploadException {
for (int i = 0; i < this.filesToUpload.length && !this.stop; i++) {
if (null != this.progressBar) {
this.progressBar.setValue(i * 100);
this.progressBar.setString(String.format(this.uploadPolicy
.getString("preparingFile"), new Integer(i + 1),
new Integer(this.filesToUpload.length)));
}
this.filesToUpload[i].beforeUpload();
// totalFilesLength is used to correctly displays the progressBar
// bar.
this.totalFilesLength += this.filesToUpload[i].getRemainingLength();
}
if (null != this.progressBar) {
this.progressBar.setValue(0);
// Setting the maximum value of the progress bar to the total length
// of all files could easily overflow the int, so we use percent
// units here.
this.progressBar.setMaximum(100);
this.progressBar.setString("");
}
}
/**
* This methods upload overhead for the file number indexFile in the
* filesDataParam given to the constructor. For instance, in HTTP, the
* upload contains a head and a tail for each files.
*
* @param indexFile The index of the file in the filesDataParam array, whose
* addtional length is asked.
* @return The additional number of bytes for this file.
*/
abstract long getAdditionnalBytesForUpload(int indexFile)
throws JUploadIOException;
/**
* This method is called before starting of each request. It can be used to
* prepare any work, before starting the request. For instance, in HTTP, the
* tail must be properly calculated, as the last one must be different from
* the others.
*
* @param firstFileToUploadParam
* @param nbFilesToUploadParam
*/
abstract void beforeRequest(int firstFileToUploadParam,
int nbFilesToUploadParam) throws JUploadException;
/**
* This method is called for each upload request to the server. The number
* of request to the server depends on: <DIR>
* <LI>The total number of files to upload.
* <LI>The value of the nbFilesPerRequest applet parameter.
* <LI>The value of the maxChunkSize applet parameter. </DIR> The main
* objective of this method is to open the connection to the server, where
* the files to upload will be written. It should also send any header
* necessary for this upload request. The {@link #getOutputStream()} methods
* is then called to know where the uploaded files should be written. <BR>
* Note: it's up to the class containing this method to internally manage
* the connection.
*
* @param contentLength The total number of bytes for the files (or the
* chunk) to upload in this query.
* @param bChunkEnabled True if this upload is part of a file (can occurs
* only if the maxChunkSize applet parameter is set). False
* otherwise.
* @param chunkPart The chunk number. Should be ignored if bChunkEnabled is
* false.
* @param bLastChunk True if in chunk mode, and this upload is the last one.
* Should be ignored if bChunkEnabled is false.
*/
abstract void startRequest(long contentLength, boolean bChunkEnabled,
int chunkPart, boolean bLastChunk) throws JUploadException;
/**
* This method is called at the end of each request.
*
* @return The response status code from the server (200 == OK)
* @see #startRequest(long, boolean, int, boolean)
*/
abstract int finishRequest() throws JUploadException;
/**
* This method is called before sending the bytes corresponding to the file
* whose index is given in argument. If the file is splitted in chunks (see
* the maxChunkSize applet parameter), this method is called before each
* chunk for this file.
*
* @param index The index of the file that will be sent just after
*/
abstract void beforeFile(int index) throws JUploadException;
/**
* Idem as {@link #beforeFile(int)}, but is called after each file (and
* each chunks for each file).
*
* @param index The index of the file that was just sent.
*/
abstract void afterFile(int index) throws JUploadException;
/**
* Clean any used resource of the last executed request. In HTTP mode, the
* output stream, input stream and the socket should be cleaned here.
*/
abstract void cleanRequest() throws JUploadException;
/**
* Clean any used resource, like a 'permanent' connection. This method is
* called after the end of the last request (see on the top of this page for
* details).
*/
abstract void cleanAll() throws JUploadException;
/**
* Get the output stream where the files should be written for upload.
*
* @return The target output stream for upload.
*/
abstract OutputStream getOutputStream() throws JUploadException;
/**
* Return the the body for the server response. That is: the server response
* without the http header. This the real functionnal response from the
* server application, that would be outputed, for instance, by any 'echo'
* PHP command.
*/
abstract String getResponseBody();
/**
* Add a String that has been read from the server response.
*
* @param msg The server message to be set.
*/
void setResponseMsg(String msg) {
this.responseMsg = msg;
}
// ////////////////////////////////////////////////////////////////////////////////////
// /////////////////////// PRIVATE FUNCTIONS
// /////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////
/**
* Retrieve the start time of this thread.
*
* @return The time this thread was started in ms.
*/
public final long getStartTime() {
return this.startTime;
}
/**
* The heart of the program. This method prepare the upload, then calls
* doUpload for each HTTP request.
*
* @see java.lang.Thread#run()
*/
@Override
final public void run() {
boolean bUploadOk = true;
// Let's read up to date upload parameters.
// These parameters may be changed by the subclasses parameter of this
// class: so we read them as late as possible, that is: here!
this.maxChunkSize = uploadPolicy.getMaxChunkSize();
this.nbMaxFilesPerUpload = uploadPolicy.getNbFilesPerRequest();
boolean stopAfterEachFile = uploadPolicy.getOneFilePerStart();
// this inhibits status-update (progress bar and status bar)
// from within the timer loop.
this.startTime = 0;
this.uploadedLength = 0;
this.totalFilesLength = 0;
try {
if (null != this.progressBar) {
this.progressBar.setValue(0);
// We allow percentage to be change, for each uploaded file.
// This allows to show how picture preparation is progressing to
// the user.
this.progressBar.setMaximum(this.filesToUpload.length * 100);
}
// Prepare upload, for all files to be uploaded.
beforeUpload();
this.startTime = System.currentTimeMillis();
beforeRequest(0, this.filesToUpload.length);
// Let's go through all files.
int iFirstFileForThisUpload = 0;
int iNbFilesForThisUpload = 0;
int currentFile = 0;
int fileUploadCount = 0;
long nextUploadContentLength = 0; // The contentLength of file
// managed byt the current loop.
long currentUploadContentLength = 0;// The current contentLength of
// files between
// iFirstFileForThisUpload and
// iNbFilesForThisUpload
// ////////////////////////////////////////////////////////////////////////////////////////
// We upload files, according to the current upload policy.
// WHILE
while (iFirstFileForThisUpload + iNbFilesForThisUpload < this.filesToUpload.length
&& bUploadOk && !this.stop && (!stopAfterEachFile || (fileUploadCount<1))) {
currentFile = iFirstFileForThisUpload + iNbFilesForThisUpload;
// Calculate the size of this file upload
nextUploadContentLength = this.filesToUpload[currentFile]
.getRemainingLength()
+ getAdditionnalBytesForUpload(currentFile);
// If we already had one or more files to upload, and the new
// upload content length is more
// the the maxChunkSize, we upload what we already have to.
if (iNbFilesForThisUpload > 0
&& currentUploadContentLength + nextUploadContentLength > this.maxChunkSize
&& !this.stop) {
// Let's do an upload.
bUploadOk = doUpload(iFirstFileForThisUpload,
iNbFilesForThisUpload);
iFirstFileForThisUpload += iNbFilesForThisUpload;
iNbFilesForThisUpload = 0;
currentUploadContentLength = 0;
}
// Let's add the current file to the list of files for the next
// upload.
currentUploadContentLength += nextUploadContentLength;
iNbFilesForThisUpload += 1;
// If the current file is bigger than the maxChunkSize:
// a) We did an upload in the previous 'if'.
// b) We upload this file alone, and it will use chunks (see
// doUpload).
if (currentUploadContentLength > this.maxChunkSize
&& !this.stop) {
// Let's do an upload.
bUploadOk = doUpload(iFirstFileForThisUpload,
iNbFilesForThisUpload);
iFirstFileForThisUpload += iNbFilesForThisUpload;
iNbFilesForThisUpload = 0;
currentUploadContentLength = 0;
}
// Do we attain the maximum number of files in one upload ?
if (iNbFilesForThisUpload == this.nbMaxFilesPerUpload
&& !this.stop) {
// Let's do an upload.
bUploadOk = doUpload(iFirstFileForThisUpload,
iNbFilesForThisUpload);
iFirstFileForThisUpload += iNbFilesForThisUpload;
iNbFilesForThisUpload = 0;
currentUploadContentLength = 0;
}
fileUploadCount++; // if we're keeping track, only once through the loop...
}// while
if (iNbFilesForThisUpload > 0 && bUploadOk && !this.stop) {
// Some files are still to upload. Let's finish the job.
bUploadOk = doUpload(iFirstFileForThisUpload,
iNbFilesForThisUpload);
}
// Let's show everything is Ok
if (null != this.progressBar) {
if (bUploadOk || this.stop) {
if (!this.stop)
this.progressBar.setString(String.format(
this.uploadPolicy.getString("nbUploadedFiles"),
new Integer(iFirstFileForThisUpload
+ iNbFilesForThisUpload)));
else
this.progressBar.setString(String.format(
this.uploadPolicy.getString("infoAborted"),
new Integer(iFirstFileForThisUpload - 1)));
} else {
this.progressBar.setString(this.uploadPolicy
.getString("errDuringUpload"));
}
}
} catch (JUploadException e) {
bUploadOk = false;
this.uploadException = e;
this.uploadPolicy.displayErr(e);
if (null!=this.progressBar) this.progressBar.setString(e.getMessage());
// TODO report f.errorUpload();
} finally {
// In all cases, we try to free all reserved resources.
this.uploadPolicy.displayDebug(
"FileUploadThread: within run().finally", 70);
try {
UploadFileData f;
for (int i = 0; i < this.filesToUpload.length; i++) {
f = this.filesToUpload[i];
if (f != null) {
f.afterUpload();
}
}
} catch (Exception e) {
this.uploadPolicy.displayWarn(e.getClass().getName() + " in "
+ getClass().getName() + ".run() (finally)");
}
}
// If the upload was unsuccessful, we try to alert the webmaster.
if (!bUploadOk && !this.stop) {
this.uploadPolicy.sendDebugInformation("Error in Upload");
}
// Enf of thread.
}// run
/**
* Actual execution file upload. It's called by the run methods, once for
* all files, or file by file, depending on the UploadPolicy. <BR>
* This method is called by the run() method. The prerequisite are : <DIR>
* <LI>If the contentLength for the nbFilesToUploadParam is more than the
* maxChunkSize, then nbFilesToUploadParam is one.
* <LI>nbFilesToUploadParam is less (or equal) than the
* nbMaxFilesPerUpload. </DIR>
*
* @param firstFileToUploadParam The index of the first file to upload, in
* the {@link #filesToUpload} area.
* @param nbFilesToUploadParam Number of file to upload, in the next HTTP
* upload request. These files are taken from the
* {@link #filesToUpload} area
*/
final private boolean doUpload(int firstFileToUploadParam,
int nbFilesToUploadParam) {
boolean bReturn = true;
boolean bLastChunk = false;
boolean bChunkEnabled = false;
int chunkPart = 0;
int nbFilesToUpload = 0;
long totalContentLength = 0;
long totalFileLength = 0;
long contentLength = 0;
long thisChunkSize = 0;
int firstFileToUpload = 0;
String msg;
if (nbFilesToUploadParam == 1) {
msg = (firstFileToUploadParam + 1) + "/"
+ (this.filesToUpload.length);
} else {
msg = (firstFileToUploadParam + 1) + "-"
+ (firstFileToUploadParam + nbFilesToUploadParam) + "/"
+ (this.filesToUpload.length);
}
if (!this.stop && (null != this.progressBar)) {
this.progressBar.setString(String.format(this.uploadPolicy
.getString("infoUploading"), msg));
}
// Let's be optimistic: we calculate the total upload length. Then,
// we'll test that this is less that the
// maximum chunk size ... if any is defined.
try {
// Prepare upload, for all files to be uploaded.
// We have to do it again, in case we don't upload all files at
// once. In HTTP header the last tail
// is different from the other one.
beforeRequest(firstFileToUploadParam, nbFilesToUploadParam);
for (int i = 0; i < nbFilesToUploadParam && !this.stop; i++) {
// Total length, for HTTP upload.
totalContentLength += this.filesToUpload[firstFileToUploadParam
+ i].getUploadLength();
totalContentLength += getAdditionnalBytesForUpload(firstFileToUploadParam
+ i);
// Total file length: used to manage the progess bar (we don't
// follow the bytes uploaded within headers and forms).
totalFileLength += this.filesToUpload[firstFileToUploadParam
+ i].getUploadLength();
this.uploadPolicy.displayDebug("file "
+ (firstFileToUploadParam + i)
+ ": content="
+ this.filesToUpload[firstFileToUploadParam + i]
.getUploadLength()
+ " bytes, getAdditionnalBytesForUpload="
+ getAdditionnalBytesForUpload(firstFileToUploadParam
+ i) + " bytes", 80);
}// for
} catch (JUploadException e) {
this.uploadPolicy.displayErr(e);
this.uploadException = e;
}
// Ok, now we check that the totalContentLength is less than the chunk
// size.
if (totalFileLength >= this.maxChunkSize) {
// hum, hum, we have to download file by file, with chunk enabled.
// This a prerequisite of this method.
if (nbFilesToUploadParam > 1) {
this.uploadException = new JUploadException(
"totalContentLength >= chunkSize: nbFilesToUploadParam should be more than 1 (doUpload)");
}
bChunkEnabled = true;
}
// This while enables the chunk management:
// In chunk mode, it loops until the last chunk is uploaded. This works
// only because, in chunk mode,
// files are uploaded one y one (the for loop within the while loops
// through ... 1 unique file).
// In normal mode, it does nothing, as the bLastChunk is set to true in
// the first test, within the while.
while (!bLastChunk && this.uploadException == null && !this.stop) {
try {
// First: chunk management.
if (bChunkEnabled) {
// Let's manage chunk:
// Files are uploaded one by one. This is checked just
// above.
firstFileToUpload = firstFileToUploadParam;
nbFilesToUpload = 1;
chunkPart += 1;
bLastChunk = (contentLength > this.filesToUpload[firstFileToUploadParam]
.getRemainingLength());
// Is this the last chunk ?
if (bLastChunk) {
thisChunkSize = this.filesToUpload[firstFileToUploadParam]
.getRemainingLength();
} else {
thisChunkSize = this.maxChunkSize;
}
contentLength = thisChunkSize
+ getAdditionnalBytesForUpload(firstFileToUploadParam);
} else {
// Chunk not activate. We upload all files at once.
bLastChunk = true;
contentLength = totalContentLength;
firstFileToUpload = firstFileToUploadParam;
nbFilesToUpload = nbFilesToUploadParam;
}
// Ok, we've prepare the job for chunk upload. Let's do it!
bytesReported = 0; // init to zero...
msecsReported = (new Date().getTime());
startRequest(contentLength, bChunkEnabled, chunkPart,
bLastChunk);
for (int i = 0; i < nbFilesToUpload && !this.stop; i++) {
// Write to Server the head(4 Lines), a File and the tail.
// do javascript callbacks
this.filesToUpload[firstFileToUpload+i].uploadStart();
// Let's add any file-specific header.
beforeFile(firstFileToUpload + i);
// In chunk mode, we already calculate the correct
// chunkSize.
if (!bChunkEnabled) {
// If not chunk mode, then the file is uploaded in one
// shot.
thisChunkSize = this.filesToUpload[firstFileToUpload
+ i].getUploadLength();
}
// Actual upload of the file:
this.currentFileData = this.filesToUpload[firstFileToUpload + i];
this.filesToUpload[firstFileToUpload + i].uploadFile(
getOutputStream(), thisChunkSize);
// If we are not in chunk mode, or if it was the last chunk,
// upload should be finished.
if (!bChunkEnabled || bLastChunk) {
if (!this.stop && (null != this.progressBar))
this.progressBar.setString(String
.format(this.uploadPolicy
.getString("infoUploaded"), msg));
if (this.filesToUpload[firstFileToUpload + i]
.getRemainingLength() > 0) {
this.uploadException = new JUploadExceptionUploadFailed(
"Files has not be entirely uploaded. The remaining size is "
+ this.filesToUpload[firstFileToUpload
+ i].getRemainingLength()
+ " bytes. File size was: "
+ this.filesToUpload[firstFileToUpload
+ i].getUploadLength()
+ " bytes.");
}
}
// Let's add any file-specific header.
afterFile(firstFileToUpload + i);
}
getOutputStream().flush();
// Let's finish the request, and wait for the server Output, if
// any (not applicable in FTP)
int status = finishRequest();
// We now ask to the uploadPolicy, if it was a success.
// If not, the isUploadSuccessful should raise an exception.
if (!this.stop){
this.uploadPolicy.checkUploadSuccess(status,
getResponseMsg(), getResponseBody());
if (1 == nbFilesToUploadParam) { // we won't get here if an exception is raised...
this.filesToUpload[firstFileToUpload].uploadSuccess(getResponseBody()); // UPLOAD_STOPPED - javascript callback
this.nbSuccessfulUploads +=1;
}
} else {
if (1 == nbFilesToUploadParam) {
this.filesToUpload[firstFileToUpload].uploadError(-290, "File Upload Stopped"); // UPLOAD_STOPPED - javascript callback
this.nbCancelledUploads+=1;
//this.filesToUpload[firstFileToUpload].uploadComplete();
}
}
} catch (Exception e) {
this.uploadException = e;
bReturn = false;
if (1 == nbFilesToUploadParam) {
this.filesToUpload[firstFileToUpload].uploadError(-200,"File Upload Failed "+getResponseMsg());
this.nbErrorUploads+=1;
//this.filesToUpload[firstFileToUpload].uploadComplete();
}
/*
* The error will be managed by the main thread. We just store
* it, for now. this.uploadPolicy.displayErr(this.uploadPolicy
* .getString("errDuringUpload"), e);
*/
}
if (1 == nbFilesToUploadParam)
this.filesToUpload[firstFileToUpload].uploadComplete( );
// Debug output: always called, so that the debug file is correctly
// filled.
this.uploadPolicy.displayDebug(
"-------- Response Body Start --------", 80);
this.uploadPolicy.displayDebug(quoteCRLF(getResponseBody()), 80);
this.uploadPolicy.displayDebug(
"--------- Response Body End ---------", 80);
} // while(!bLastChunk && uploadException==null && !stop)
try {
cleanRequest();
} catch (JUploadException e) {
this.uploadException = e;
bReturn = false;
}
if (this.uploadException == null) {
// The upload was Ok, we remove the uploaded files from the
// filePanel.
for (int i = 0; i < nbFilesToUpload && !this.stop; i++) {
this.uploadPolicy.getApplet().getUploadPanel().getFilePanel()
.remove(this.filesToUpload[firstFileToUpload + i]);
}
} else {
this.uploadPolicy.displayErr(this.uploadException);
}
return bReturn;
}
/** @see FileUploadThread#close() */
public void close() {
try {
cleanAll();
} catch (JUploadException e) {
this.uploadPolicy.displayErr(e);
}
}
/**
* Replace \r and \n by correctly displayed end of line characters. Used to display debug ouptut.
*
* @param s The original string
* @return The string with \r and \n modified, to be correctly displayed.
*/
public final String quoteCRLF(String s) {
return s.replaceAll("\r", "\\\\r").replaceAll("\n", "\\\\n\n");
}
}