/**
 * MCZUploadServlet.java
 *
 * This is a simple implementation of a file upload servlet.
 *
 * However, it has been very functional to me. 
 * It reads an incoming file stream and, after saving data into a temp file, parses
 * this one into a final file.
 * Example:
 *
 * The browser, of couse, must show a form with the input type=file tag, like this:
 *
 * <form enctype='multipart/form-data' method=post action='yourservleturl/MCZUploadServlet'>
 * <input type=file name='file1'><input type=submit>
 * </form>
 *
 * Created: Thu Apr 20 08:47:01 2000
 *
 * @author Andre de Lima Soares sfreire_@yahoo.com / alsoares@vcnet.com.br / jinsight@zipmail.com.br
 * @version 1.0
 */


import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.Date;

public class MCZUploadServlet extends HttpServlet {
    
    public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException {

	FileOutputStream tempOut=null;
	BufferedReader buff=null;
	RandomAccessFile raf=null;
	FileOutputStream fOut=null;
	ServletInputStream si=null;
	ByteArrayOutputStream ba=null;
	PrintWriter pw=null;
	String line=null;
	String fileName=null;
	String boundary=null;
	String first500=null;
	int pos500=0;
	int posFooter=0;
	int totLen=0;
	int bytesRead=0;
	boolean endReached=false;
	byte[] b=new byte[8192];
	int c=0;
	// These variables keep the file data initial and final position
	int posInit=0;
	int posEnd=0;

	// The name of the temp file is based on the present time, in milisseconds.
       	String tempFile=Long.toString((new Date().getTime()))+".tmp";
	String tempDir="/home/resin/";

	try {

	    res.setContentType("text/html");
	    pw=res.getWriter();
	    
	    si =req.getInputStream();
	    
	    // Writes bytes into a temp file
	   tempOut=new FileOutputStream(tempDir+tempFile);
	    while((c=si.read(b,0,8192)) != -1 ) {
		tempOut.write(b,0,c);
		totLen+=c;
	    }
	    
	    // Opens temp file to fetch initial block of 500 bytes and extract boundary string
	    buff=new BufferedReader(new InputStreamReader(new FileInputStream(tempDir+tempFile)));
	    	    
	    // Fetches boundary string. It is usually the first line in the stream
	    boundary=buff.readLine();
	    posInit+=boundary.length()+2; // Computes a CR+LF after boundary
	    
	    // Loops until find a double CR+LF.
	    while (!((line=buff.readLine()).equals(""))) {
		
		posInit+=line.length()+2; // Computes a CR+LF that comes after header
		
		// Extracts file name.
		if (line.startsWith("Content-Disposition")) {
		    int posFileName=line.indexOf("filename=\"");
		    fileName=line.substring(posFileName);

		    fileName=fileName.substring(fileName.indexOf("\"")+1,fileName.length()-1);

		    // MIExplorer sets the filename with its full directory path.
		    // Extracts, in this case, just the file name.
		    int slashLastIndex=fileName.lastIndexOf("\\");
		    if (slashLastIndex!=-1)
			fileName=fileName.substring(slashLastIndex+1);

		}
	    }
	    
	    // A final CR+LF must be considered to calculate the initial position of file data
	    posInit+=2;
	    
	    // Reopens temp file with RandomAccessFile to search for the final file data position.
	    raf=new RandomAccessFile(tempDir+tempFile,"rw");
	    
	    // Creates a ByteArrayOutputStream to store the last 500 bytes.
	    ba=new ByteArrayOutputStream(8192);
	    
	    // The file pointer is positioned at the beginning of the last 500 bytes.
	    pos500=(totLen-500<0 ? 0 : totLen-500);
	    raf.skipBytes(pos500);
	    
	    // Starts storing the last 500 bytes int the ByteArrayOutputStream
	    while((c=raf.read(b,0,b.length))!=-1)
		ba.write(b,0,c);
	    
	    
	    // Now looks for the footer position. It's a string identical to the boundary string,
	    // just finished with two '-' characters. (Looks like ----------------9382347239120--)
	    posFooter=ba.toString().indexOf(boundary);
	    
	    // Calculates the position of the footer relative to the entire file. Subtracts a CR+LF
	    // that precedes the footer.
	    posEnd=pos500+(posFooter-2);
	    
	    // Begins file saving, from posInit to posEnd positions.
	    // Moves file pointer to the calculated initial position
	    raf.seek(posInit);
	    fOut=new FileOutputStream(tempDir+fileName);
	    
	    bytesRead=posInit;
	    endReached=false;
	    
	    while((c=raf.read(b,0,b.length))!=-1) {
		bytesRead+=c;
		
		// If the total of bytes read is higher than the calculated end position,
		// adjusts the variable c to write bytes only contained in the posInit--posEnd range.
		if (bytesRead>posEnd) {
		    c-=(bytesRead-posEnd);
		    endReached=true;
		}
		fOut.write(b,0,c);
		if (endReached)
		    break;
	    }

	    // Deletes temporary file
	    File f=new File(tempDir+tempFile);
	    f.delete();
	    
	    pw.println("Arquivo transferido com sucesso.");
	    
	}
	catch (IOException e) {
	    pw.println(e.getMessage());
	}
	finally {
	    try {
		if (pw!=null)
		    pw.close();
		if (tempOut!=null)
		    tempOut.close();
		if (buff!=null)
		    buff.close();
		if (raf!=null)
		    raf.close();
		if (fOut!=null)
		    fOut.close();
	    }
	    catch (IOException e) {
	    }
	}
	
    }
}
	    


