cn.clxy.upload.UploadFileService.java Source code

Java tutorial

Introduction

Here is the source code for cn.clxy.upload.UploadFileService.java

Source

/**
 * Copyright (C) 2013 CLXY Studio.
 * This content is released under the (Link Goes Here) MIT License.
 * http://en.wikipedia.org/wiki/MIT_License
 */
package cn.clxy.upload;

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class UploadFileService {

    private File file;
    private BlockingQueue<Part> parts;
    private List<Integer> indexes;

    private Listener listener = new Listener.Default();
    private Uploader uploader = new ApacheHCUploader();
    private ExecutorService executor = Executors.newFixedThreadPool(Config.maxUpload);

    private static final Log log = LogFactory.getLog(UploadFileService.class);

    public UploadFileService(String fileName) {
        file = new File(fileName);
        if (!file.exists() || !file.isFile()) {
            throw new RuntimeException("File:" + file + " isn't correct!");
        }
    }

    public void upload() {
        try {
            doUpload();
        } finally {
            stop();
        }
    }

    public void retry(Integer... array) {

        // sort first.
        indexes = Arrays.asList(array);
        Collections.sort(indexes);

        try {
            doUpload();
        } finally {
            stop();
        }
    }

    public void stop() {
        if (executor != null) {
            executor.shutdown();
        }
    }

    private void doUpload() {

        listener.onStart(indexes != null ? indexes.size() : getPartCount());
        parts = new ArrayBlockingQueue<Part>(Config.maxRead);
        CompletionService<String> cs = new ExecutorCompletionService<String>(executor);

        cs.submit(readTask);

        for (int i = 0; i < Config.maxUpload; i++) {
            cs.submit(new UploadTask("upload." + i));
        }

        // Wait all done. total count = maxUpload + 1.
        for (int i = 0; i <= Config.maxUpload; i++) {
            Future<String> future = null;
            try {
                future = cs.take();
                checkFuture(future);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        // Notify sever all done.
        Future<String> result = executor.submit(notifyTask);
        checkFuture(result);
        listener.onSuccess();
    }

    private String checkFuture(Future<String> future) {

        String result = null;
        try {
            result = future.get();
            return result;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } catch (ExecutionException e) {
            listener.onFail(result);
            log.error(e.getCause());
            throw new RuntimeException(e.getCause());
        }
    }

    protected int getPartCount() {
        long length = file.length();
        long count = (length / Config.partSize) + (length % Config.partSize == 0 ? 0 : 1);
        return (int) count;
    }

    private Callable<String> readTask = new Callable<String>() {

        @Override
        public String call() throws Exception {

            FileInputStream fis = null;
            String fileName = file.getName();
            int partSize = Config.partSize;

            try {
                fis = new FileInputStream(file);
                FileChannel fc = fis.getChannel();
                for (int i = 0;; i++) {
                    ReadStatus status = getReadStatus(i, indexes);
                    if (status == ReadStatus.stop) {
                        break;
                    }

                    if (status == ReadStatus.skip) {
                        fc.position(fc.position() + partSize);
                        continue;
                    }

                    if (status == ReadStatus.read) {
                        ByteBuffer bb = ByteBuffer.allocate(partSize);
                        int bytesRead = fc.read(bb);
                        if (bytesRead == -1) {
                            break;
                        }
                        byte[] bytes = bb.array();
                        if (bytesRead != partSize) {// trim
                            bytes = Arrays.copyOf(bytes, bytesRead);
                        }
                        String partName = createFileName(fileName, i);
                        listener.onRead(partName);
                        parts.put(new Part(partName, bytes));
                    }
                }
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (Exception e) {
                    }
                }
                parts.put(Part.NULL);// put end signal.
            }

            return "read";
        }

        /**
         * Create file name of part. <br>
         * bigfile.avi = [bigfile.avi<strong>.0</strong>, bigfile.avi.1, bigfile.avi.2 ...]
         * @param fileName
         * @param i
         * @return
         */
        protected String createFileName(String fileName, int i) {
            return fileName + "." + i;// start by 0.
            // return fileName + (i == 0 ? "" : ("." + i));
        }

        private ReadStatus getReadStatus(int i, List<Integer> indexes) {

            if (indexes == null || indexes.contains(i)) {
                return ReadStatus.read;
            }

            if (i > indexes.get(indexes.size() - 1)) {
                return ReadStatus.stop;
            }

            return ReadStatus.skip;
        }
    };

    private static enum ReadStatus {
        stop, skip, read
    }

    private class UploadTask implements Callable<String> {

        private String name;

        public UploadTask(String name) {
            this.name = name;
        }

        @Override
        public String call() throws Exception {

            while (true) {

                Part part = parts.take();
                if (part == Part.NULL) {
                    parts.add(Part.NULL);// notify others to stop.
                    break;
                }

                String partName = part.getName();
                listener.onUpload(partName);
                uploader.upload(part);
                listener.onPartDone(partName);
            }
            return name;
        }
    }

    private Callable<String> notifyTask = new Callable<String>() {
        @Override
        public String call() throws Exception {
            uploader.done(file.getName(), getPartCount());
            return "notify";
        }
    };
}