All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kawanfw.file.servlet.util.FileTransferManager Maven / Gradle / Ivy

Go to download

Awake FILE is a secure Open Source framework that allows to program very easily file uploads/downloads and RPC through http. File transfers include powerful features like file chunking and automatic recovery mechanism. Security has been taken into account from the design: server side allows to specify strong security rules in order to protect the files and to secure the RPC calls.

The newest version!
/*
 * This file is part of Awake FILE. 
 * Awake file: Easy file upload & download over HTTP with Java.                                    
 * Copyright (C) 2015,  KawanSoft SAS
 * (http://www.kawansoft.com). All rights reserved.                                
 *                                                                               
 * Awake FILE 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.            
 *                                                                               
 * Awake FILE 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., 51 Franklin Street, Fifth Floor, Boston, MA  
 * 02110-1301  USA
 *
 * Any modifications to this file must keep this entire header
 * intact.
 */
package org.kawanfw.file.servlet.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.logging.Level;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.kawanfw.commons.server.util.ServerLogger;
import org.kawanfw.commons.util.DefaultParms;
import org.kawanfw.commons.util.FrameworkDebug;
import org.kawanfw.file.api.server.FileConfigurator;


/**
 * The Action Manager for files transfer: define how all concrete operations will be done
 * on files.
 * 
 * @author Nicolas de Pomereu
 * @since 1.0
 */

public class FileTransferManager {

    private static boolean DEBUG = FrameworkDebug
	    .isSet(FileTransferManager.class);

    private static final int EOF = -1;
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    /**
     * Constructor.
     */
    public FileTransferManager() {

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.kawanfw.file.api.server.fileaction.FileActionManager#
     * actionDownloadFile(java.io.OutputStream,
     * org.kawanfw.file.api.server.FileConfigurator, java.lang.String,
     * java.lang.String)
     */
 
    public boolean download(OutputStream out,
	    FileConfigurator fileConfigurator, String username,
	    String filename, long chunkLength) throws FileNotFoundException,
	    IOException {
	InputStream in = null;

	debug(new Date() + " DOWNLOAD SESSION BEGIN ");

	try {
	    filename = HttpConfigurationUtil.addRootPath(fileConfigurator,
		    username, filename);

	    debug(new Date() + " DOWNLOAD CHUNK");

	    // Do we must download a chunk only ? We will then seek the
	    // Random access file and read only one chunk length and send it
	    // back to client
	    if (filename.endsWith(".kawanfw.chunk")) {

		// We are now in chunk case
		String rawFilename = StringUtils.substringBeforeLast(filename,
			".kawanfw.chunk");
		String indexStr = StringUtils.substringAfterLast(rawFilename,
			".");
		int index = Integer.parseInt(indexStr);

		// Remove the number
		rawFilename = StringUtils.substringBeforeLast(rawFilename, ".");

		// We seek the total length of previous files, because client
		// method
		// is idempotent and may be replayed
		long lengthToSeek = (index - 1) * chunkLength;

		// debug("index       : " + index);
		// debug("chunkLength : " + chunkLength);
		// debug("lengthToSeek: " + lengthToSeek);

		debug("");
		debug(new Date() + " SESSION " + " " + index);

		RandomAccessFile raf = null;

		try {

		    File file = new File(rawFilename);

		    if (!file.exists()) {
			debug("File does not exists: " + file);
			return false;
		    }

		    debug(new Date() + " BEFORE SEEK ");

		    debug(new Date() + " BEFORE CREATE RAF");
		    raf = new RandomAccessFile(file, "rw");
		    debug(new Date() + " AFTER  CREATE RAF");

		    raf.seek(lengthToSeek);

		    debug(new Date() + " BEFORE COPY ");
		    long totalRead = copy(raf, out, chunkLength);
		    debug(new Date() + " AFTER COPY " + totalRead);

		    IOUtils.closeQuietly(raf);

		    if (lengthToSeek + totalRead >= file.length()) {
			// End of operations
			// Nothing yo do with Random Access File
		    }

		} finally {
		    IOUtils.closeQuietly(raf);
		}

		return true;

	    } else {

		debug(new Date() + " DOWNLOAD FULL FILE");

		File file = new File(filename);

		if (!file.exists()) {
		    debug("File does not exists: " + file);
		    return false;
		}

		in = new BufferedInputStream(new FileInputStream(file));
		IOUtils.copy(in, out);
	    }

	    return true;

	} finally {
	    IOUtils.closeQuietly(in);
	}

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.kawanfw.file.api.server.fileaction.FileActionManager#
     * actionUploadFile(org.kawanfw.file.api.server.FileConfigurator,
     * java.io.InputStream, java.lang.String, java.lang.String)
     */
    public void upload(FileConfigurator fileConfigurator,
	    InputStream inputStream, String username, String filename,
	    long chunkLength) throws IOException {

	debug(new Date() + " UPLOAD SESSION BEGIN ");

	filename = HttpConfigurationUtil.addRootPath(fileConfigurator,
		username, filename);

	// is it a file chunk? If yes append to filename
	if (filename.endsWith(".kawanfw.chunk")
		|| filename.endsWith(".kawanfw.chunk.LASTCHUNK")) {

	    RandomAccessFile raf = null;

	    try {

		boolean lastChunk = false;

		if (filename.endsWith(".LASTCHUNK")) {
		    debug(new Date() + " RENAME DONE");
		    filename = StringUtils.substringBeforeLast(filename,
			    ".LASTCHUNK");
		    lastChunk = true;
		}

		initFileIfFirstChunk(username, filename);

		String rawFilename = StringUtils.substringBeforeLast(filename,
			".kawanfw.chunk");
		String indexStr = StringUtils.substringAfterLast(rawFilename,
			".");

		// Remove the number
		rawFilename = StringUtils.substringBeforeLast(rawFilename, ".");

		int index = Integer.parseInt(indexStr);

		File file = new File(rawFilename);

		debug(new Date() + " SESSION INDEX" + " " + index);

		// We must create, if necessary, the path to the file
		createParentDir(file);

		debug(new Date() + " BEFORE CREATE RAF");
		raf = new RandomAccessFile(file, "rw");
		debug(new Date() + " AFTER CREATE RAF");

		// We seek the total length of previous files, because client
		// method
		// is idempotent and may be replayed
		long lengthToSeek = (index - 1) * chunkLength;

		// debug("index       : " + index);
		// debug("chunkLength : " + chunkLength);
		// debug("lengthToSeek: " + lengthToSeek);

		debug(new Date() + " BEFORE SEEK ");
		raf.seek(lengthToSeek);

		debug(new Date() + " BEFORE COPY ");
		copy(inputStream, raf, new byte[DEFAULT_BUFFER_SIZE]);
		debug(new Date() + " AFTER COPY ");

		IOUtils.closeQuietly(raf);

		if (lastChunk) {
		    // End of operations
		    // Do nothing with Random Access Files
		}

	    } finally {
		IOUtils.closeQuietly(raf);
	    }

	} else {

	    OutputStream out = null;

	    try {
		File file = new File(filename);

		// We must create, if necessary, the path to the file
		createParentDir(file);

		out = new BufferedOutputStream(new FileOutputStream(file));
		IOUtils.copy(inputStream, out);

		debug("file created : " + file);
		debug("file.length(): " + file.length());
	    } finally {
		IOUtils.closeQuietly(out);
	    }
	}

    }

    /**
     * Copy the content of the random access file to the output stream up to a
     * chubk length
     * 
     * @param raf
     * @param out
     * @param chunkLength
     * @return the total amount read
     * 
     * @throws IOException
     */
    private long copy(RandomAccessFile raf, OutputStream out, long chunkLength)
	    throws IOException {
	int writeBufferSize = DefaultParms.DEFAULT_WRITE_BUFFER_SIZE;

	// For Chunk Length comparison. If buffer read > chunk
	// length ==> exit
	long totalRead = 0;

	byte[] tmp = new byte[writeBufferSize];
	int len;

	while ((len = raf.read(tmp)) >= 0) {
	    totalRead += len;
	    out.write(tmp, 0, len);

	    // Exit if chunk length is reached. We will just send
	    // the chunck,InputStream will be reused
	    if (chunkLength > 0 && totalRead > chunkLength - writeBufferSize) {
		return totalRead;
	    }
	}

	return totalRead;
    }

    /**
     * Copy the input stream into the raf
     * 
     * @param input
     * @param output
     * @param buffer
     * @return the lrngth written into the reaf
     * @throws IOException
     */
    private long copy(InputStream input, RandomAccessFile output, byte[] buffer)
	    throws IOException {
	long count = 0;
	int n = 0;
	while (EOF != (n = input.read(buffer))) {
	    output.write(buffer, 0, n);
	    count += n;
	}
	return count;
    }

    /**
     * Create the parent directory of file, if necessary
     * 
     * @param file
     *            the file to create for the parent directories if necessary
     */
    public void createParentDir(File file) {
	// We must create, if necessary, the path to the file
	File parentDir = file.getParentFile();
	if (parentDir != null) {
	    parentDir.mkdirs();
	}
    }

    /**
     * Delete the raw file if first chunk
     * 
     * @param username
     *            the client username
     * @param filename
     *            the chunk file name
     */
    private void initFileIfFirstChunk(String username, String filename)
	    throws IOException {

	if (filename.endsWith(".1.kawanfw.chunk")) {
	    String rawFilename = StringUtils.substringBeforeLast(filename,
		    ".kawanfw.chunk");
	    rawFilename = StringUtils.substringBeforeLast(rawFilename, "."); // Remove
									     // the
									     // number

	    File file = new File(rawFilename);

	    if (file.exists()) {
		boolean deleted = file.delete();
		if (!deleted) {
		    throw new IOException(
			    "File delete required because of upload of first chunk. Impossible to delete file: "
				    + file);
		}
	    }
	}

    }

   
    private void debug(String s) {
	if (DEBUG) {
	    ServerLogger.getLogger().log(Level.WARNING, s);
	}
    }

 
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy