com.polyvi.xface.extension.advancedfiletransfer.FileUploader.java Source code

Java tutorial

Introduction

Here is the source code for com.polyvi.xface.extension.advancedfiletransfer.FileUploader.java

Source

/*
 Copyright 2012-2013, Polyvi Inc. (http://polyvi.github.io/openxface)
 This program is distributed under the terms of the GNU General Public License.
    
 This file is part of xFace.
    
 xFace 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 3 of the License, or
 (at your option) any later version.
    
 xFace 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 xFace.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.polyvi.xface.extension.advancedfiletransfer;

import java.io.DataInputStream;
import java.io.File;
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.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.apache.cordova.PluginResult.Status;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;

import com.polyvi.xface.util.XConstant;
import com.polyvi.xface.util.XFileUtils;
import com.polyvi.xface.util.XLog;
import com.polyvi.xface.util.XStringUtils;

//???????Content-Length=?;filename=?;sourceid= ??sourceid).
// ???????(? sourceid=?;position=?),?position
public class FileUploader implements IFileTransferListener, IFileTransfer {

    private static final String CLASS_NAME = FileUploader.class.getSimpleName();

    /** ??? */
    private static final String ACTION_NAME_HAND = "HAND";

    /** ?? */
    private static final String ACTION_NAME_UPLOAD = "UPLOAD";

    /** http */
    private static final String HTTP_HEAD = "http://";

    /**  */
    private static final int mTimeout = 5000;

    /** ?? */

    private static final int FILE_NOT_FOUND_ERR = 1;
    private static final int INVALID_URL_ERR = 2;
    private static final int CONNECTION_ERR = 3;

    /** ??????? */
    private static final int INIT = 1;
    private static final int UPLOADING = 2;
    private static final int PAUSE = 3;
    private int mState = INIT;

    /** ???? */
    private static final int DIVIDE_SIZE_TWO = 2;
    private static final int DIVIDE_SIZE_TEN = 10;
    private static final int DIVIDE_SIZE_TWENTY = 20;
    private static final int SIZE_KB = 1024;

    /** ?? */
    private static final int READ_FILE_END = -1;

    /**  */
    private static final int RESULT_CODE_ERROR = -1;

    /** ??,1???0?? */
    private static final int RESULT_CODE_CHUNK_RECEIVED = 1;
    private static final int RESULT_CODE_FILE_RECEIVED = 0;

    /** ??file */
    private static final String PREFIX = "stream2file";
    private static final String SUFFIX = ".tmp";

    /** ?? */
    private long mUploadFileSize;

    /** ??? */
    private int mUploadFileSizePerTime;

    private Context mContext;

    /** ? */
    private String mFilePath;

    /** ? */
    private File mUploadFile;

    /** ???id */
    private String mResponseId;

    /** ?? */
    private int mStartedPosition;

    /** ??? */
    private int mAlreadyUploadLength;

    /** ?? */
    private String mServer;

    /** nativejs */
    private CallbackContext mCallbackCtx;

    /** ?? */
    private FileTransferRecorder mFileTransferRecorder;

    /** ? */
    private FileTransferManager mFileTransferManager;

    public FileUploader(Context context, String filePath, String server, FileTransferRecorder recorder,
            FileTransferManager manager) {
        mContext = context;
        mFilePath = filePath;
        mServer = server;
        mFileTransferRecorder = recorder;
        mFileTransferManager = manager;
    }

    @Override
    public void onSuccess() {
        mFileTransferRecorder.deleteUploadInfo(mFilePath);
        setState(INIT);
        mFileTransferManager.removeFileTranferTask(mFilePath);
        mCallbackCtx.success();
    }

    @Override
    public void onError(int errorCode) {
        setState(INIT);
        JSONObject error = new JSONObject();
        Status status = Status.ERROR;
        try {
            error.put("code", errorCode);
            error.put("source", mFilePath);
            error.put("target", mServer);
        } catch (JSONException e) {
            status = Status.JSON_EXCEPTION;
            XLog.e(CLASS_NAME, e.getMessage());
        }
        PluginResult result = new PluginResult(status, error);
        mCallbackCtx.sendPluginResult(result);
    }

    // TODO:???
    @Override
    public void onProgressUpdated(int completeSize, long totalSize) {
        JSONObject jsonObj = new JSONObject();
        try {
            jsonObj.put("loaded", completeSize);
            jsonObj.put("total", totalSize);
        } catch (JSONException e) {
            XLog.e(CLASS_NAME, e.getMessage());
        }
        PluginResult result = new PluginResult(PluginResult.Status.OK, jsonObj);
        result.setKeepCallback(true);
        mCallbackCtx.sendPluginResult(result);
    }

    /**  */
    private void upload() {
        // URL
        if (!initUploadFileInfo()) {
            return;
        }
        byte[] buffer = new byte[mUploadFileSizePerTime];
        // ????
        while ((mAlreadyUploadLength < mUploadFileSize)) {
            // ?
            if (!handleShake()) {
                break;
            }
            // ??
            int len = readFileData(buffer);
            // 
            if (!uploadData(buffer, len)) {
                break;
            }
        }
        if (mState != PAUSE && mAlreadyUploadLength != mUploadFileSize) {
            onError(CONNECTION_ERR);
        }
    }

    /**
     * ??????id?
     *
     * @return true:??false:?
     */
    private boolean handleShake() {
        HttpURLConnection httpConnection = null;
        DataInputStream dataInputStream = null;
        String souceid = mFileTransferRecorder.getSourceId(mFilePath, "" + mUploadFileSize);
        try {
            httpConnection = getHttpConnection(mServer);
            // ?
            httpConnection.setRequestProperty("Charset", "UTF-8");
            httpConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            httpConnection.setRequestProperty("ACTIONNAME", ACTION_NAME_HAND);
            httpConnection.setRequestProperty("RESOURCEID", souceid);
            httpConnection.setRequestProperty("FILENAME", getUploadFileName());
            httpConnection.setRequestProperty("FILESIZE", "" + mUploadFileSize);
            if (HttpURLConnection.HTTP_OK == httpConnection.getResponseCode()) {
                // ????? RESOURCEID=?;BFFORE=?
                dataInputStream = new DataInputStream(httpConnection.getInputStream());
                // ???response?
                handleResponse(dataInputStream.readLine());
                // souceid?
                setSourceId(souceid);
            } else {
                onError(INVALID_URL_ERR);
            }
        } catch (Exception e) {
            onError(INVALID_URL_ERR);
            e.printStackTrace();
            return false;
        } finally {
            if (null != httpConnection) {
                httpConnection.disconnect();
                httpConnection = null;
            }
            // ??
            try {
                if (null != dataInputStream) {
                    dataInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 
     *
     * @return true??,false?
     */
    private boolean uploadData(byte[] buffer, int len) {
        HttpURLConnection httpConnection = null;
        InputStream inStream = null;
        try {
            // 
            httpConnection = getHttpConnection(mServer);
            httpConnection.setRequestProperty("Charset", "UTF-8");
            httpConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            httpConnection.setRequestProperty("ACTIONNAME", ACTION_NAME_UPLOAD);
            httpConnection.setRequestProperty("RESOURCEID", mResponseId);
            httpConnection.setRequestProperty("BEFORE", "" + mStartedPosition);
            // ??buffer?
            writeBufferData(httpConnection.getOutputStream(), buffer, len);
            // ????
            int resultCode = getResultCode(httpConnection);
            doProcessUpdate(resultCode, len);
            if ((RESULT_CODE_FILE_RECEIVED == resultCode) && (mAlreadyUploadLength == mUploadFileSize)) {
                onSuccess();
            }
            if (mState == PAUSE) {
                return false;
            }
        } catch (Exception e) {
            onError(CONNECTION_ERR);
            XLog.e(CLASS_NAME, "connection error");
            return false;
        } finally {
            if (null != inStream) {
                try {
                    inStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != httpConnection) {
                httpConnection.disconnect();
                httpConnection = null;
            }
        }
        return true;
    }

    /**
     * ??
     *
     * @return true:???,false?
     */
    private boolean initUploadFileInfo() {
        // URL
        if (null == (mUploadFile = getFile())) {
            onError(FILE_NOT_FOUND_ERR);
            return false;
        }
        if (!mServer.startsWith(HTTP_HEAD)) {
            onError(INVALID_URL_ERR);
            return false;
        }
        mUploadFileSize = getUploadFileSize();
        mUploadFileSizePerTime = getSingleUploadLength();
        return true;
    }

    /**
     * ???
     *
     * @param response
     *            ?response
     * @return true:responsefalse:response
     */
    private void handleResponse(String response) throws Exception {
        if (XStringUtils.isEmptyString(response)) {
            throw new Exception("response is null");
        }
        String items[] = response.split(";");
        int first_index = items[0].indexOf(":");
        int second_index = items[1].indexOf(":");
        if ((RESULT_CODE_ERROR == first_index) || (RESULT_CODE_ERROR == second_index)) {
            throw new Exception("response error");
        }
        mResponseId = items[0].substring(items[0].indexOf(":") + 1);
        mStartedPosition = Integer.parseInt(items[1].substring(items[1].indexOf(":") + 1));
        if (mStartedPosition > mUploadFileSize) {
            throw new Exception("file StartedPosition is bigger than  fileSize error");
        }
    }

    /**
     * souceid?
     *
     * @param souceid
     *            ?sourceid
     */
    private void setSourceId(String souceid) {
        if (null == souceid) {
            mFileTransferRecorder.saveUploadInfo(mResponseId, mFilePath, "" + mUploadFileSize);
        }

    }

    /**
     * ?buffer?,??
     *
     * @param buffer
     *            :??
     */
    private int readFileData(byte[] buffer) {
        int len = READ_FILE_END;
        RandomAccessFile accessFile = null;
        try {
            accessFile = new RandomAccessFile(mUploadFile, "r");
            accessFile.seek(mStartedPosition);
            len = accessFile.read(buffer);
            if (mStartedPosition != mAlreadyUploadLength) {
                mAlreadyUploadLength = mStartedPosition;
            }
            accessFile.close();
        } catch (FileNotFoundException e) {
            len = READ_FILE_END;
            onError(FILE_NOT_FOUND_ERR);
        } catch (IOException e) {
            len = READ_FILE_END;
            onError(FILE_NOT_FOUND_ERR);
        }
        return len;
    }

    /**
     * buff??
     *
     * @param outStream
     *            ?
     * @param buffer
     *            :??
     * @param len
     *            :??
     * @return ??
     */
    private int writeBufferData(OutputStream outStream, byte[] buffer, int len) throws IOException {
        outStream.write(buffer, 0, len);
        outStream.flush();
        outStream.close();
        return len;
    }

    /**
     * ????
     *
     * @param httpConnection
     *            :http
     * @return 1?? 0? -1??
     */
    private int getResultCode(HttpURLConnection httpConnection) {
        DataInputStream dataInputStream = null;
        try {
            if (HttpURLConnection.HTTP_OK != httpConnection.getResponseCode()) {
                return RESULT_CODE_ERROR;
            }
            // ??RETURN_CODE:1
            dataInputStream = new DataInputStream(httpConnection.getInputStream());
            // ???response?
            String data = dataInputStream.readLine();
            int resultCode = Integer.valueOf(data.substring(data.indexOf(":") + 1));
            return (resultCode == RESULT_CODE_CHUNK_RECEIVED || resultCode == RESULT_CODE_FILE_RECEIVED)
                    ? resultCode
                    : RESULT_CODE_ERROR;
        } catch (Exception e) {
            e.printStackTrace();
            return RESULT_CODE_ERROR;
        } finally {
            try {
                if (null != dataInputStream) {
                    dataInputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 
     *
     * @param len
     *            :?
     */
    private void doProcessUpdate(int resultCode, int len) {
        if (RESULT_CODE_ERROR != resultCode) {
            mAlreadyUploadLength += len;
            XLog.d("xface", "" + mAlreadyUploadLength);
            onProgressUpdated(mAlreadyUploadLength, mUploadFileSize);
        }
    }

    /** ??? */
    private long getUploadFileSize() {
        return mUploadFile.length();
    }

    /** ???? */
    private String getUploadFileName() {
        return mUploadFile.getName();
    }

    /**
     * ?2<br/>
     * 1.?????<br/>
     * 2.??<br/>
     * ????? <br/>
     * ??1k?2<br/>
     * ?1k-1M?10<br/>
     * ?1M-10M?20<br/>
     * ?10M?2M<br/>
     * */
    private int getSingleUploadLength() {
        // ?
        int totalLength = (int) mUploadFileSize;
        // ?100
        if (totalLength < SIZE_KB / DIVIDE_SIZE_TEN) {
            return SIZE_KB / DIVIDE_SIZE_TEN;
        } else if (totalLength < SIZE_KB) {
            return totalLength / DIVIDE_SIZE_TWO;
        } else if (totalLength < SIZE_KB * SIZE_KB) {
            return totalLength / DIVIDE_SIZE_TEN;
        } else if (totalLength < DIVIDE_SIZE_TEN * SIZE_KB * SIZE_KB) {
            return totalLength / DIVIDE_SIZE_TWENTY;
        } else {
            return DIVIDE_SIZE_TWO * SIZE_KB * SIZE_KB;
        }
    }

    /**
     * ?urlhttp
     *
     * @param url
     *            :url?
     * @return urlhttp
     * */
    private HttpURLConnection getHttpConnection(String url)
            throws IOException, MalformedURLException, ProtocolException {
        HttpURLConnection httpConnection;
        httpConnection = ((HttpURLConnection) new URL(url).openConnection());
        httpConnection.setConnectTimeout(mTimeout);
        httpConnection.setRequestMethod("POST");
        httpConnection.setDoInput(true);
        httpConnection.setDoOutput(true);
        return httpConnection;
    }

    @Override
    public void transfer(CallbackContext callbackCtx) {
        if (mState == UPLOADING) {
            return;
        }
        mCallbackCtx = callbackCtx;
        setState(UPLOADING);
        new Thread(new Runnable() {
            @Override
            public void run() {
                upload();
            }
        }).start();
    }

    private synchronized void setState(int state) {
        mState = state;
    }

    /**
     * ??
     */
    private File getFile() {
        try {
            if (null != mFilePath) {
                if (mFilePath.startsWith(XConstant.ANDROID_ASSET)) {
                    // ?assets
                    InputStream is = mContext.getAssets()
                            .open(mFilePath.substring(XConstant.ANDROID_ASSET.length()));
                    return createTempFile(is);
                } else {
                    File uploadFile = new File(mFilePath);
                    if (uploadFile.exists()) {
                        return uploadFile;
                    }
                }
            }
        } catch (IOException e) {
            XLog.e(CLASS_NAME, "Upload get file failed");
        } catch (IllegalArgumentException e) {
            XLog.e(CLASS_NAME, "Upload get file failed");
        }
        onError(FILE_NOT_FOUND_ERR);
        return null;
    }

    /**
     * ??file
     *
     * @param in
     * @return
     * @throws IOException
     */
    public static File createTempFile(InputStream in) throws IOException, IllegalArgumentException {
        if (null == in) {
            XLog.e(CLASS_NAME, "createTempFile methods: param is null");
            throw new IllegalArgumentException();
        }
        final File tempFile = File.createTempFile(PREFIX, SUFFIX);
        tempFile.deleteOnExit();
        FileOutputStream out = new FileOutputStream(tempFile);
        try {
            byte[] buffer = new byte[XConstant.BUFFER_LEN];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        } catch (IOException e) {
            XLog.e(CLASS_NAME, "Create temp file failed");
            throw new IOException();
        } finally {
            if (null != out) {
                out.flush();
                out.close();
            }
            in.close();
        }
        return tempFile;
    }

    @Override
    public void pause() {
        setState(PAUSE);
    }

}