Java tutorial
/* 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); } }