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.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.json.JSONException; import org.json.JSONObject; import com.polyvi.xface.extension.XCallbackContext; import com.polyvi.xface.extension.XExtensionContext; import com.polyvi.xface.extension.XExtensionResult; import com.polyvi.xface.extension.XExtensionResult.Status; import com.polyvi.xface.plugin.api.XIWebContext; import com.polyvi.xface.util.XLog; import com.polyvi.xface.util.XPathResolver; import com.polyvi.xface.util.XStringUtils; //???????Content-Length=?;filename=?;sourceid= ??sourceid). // ???????(? sourceid=?;position=?),?position public class XFileUploader implements XIFileTransfer, XIFileTransferListener { private static final String CLASS_NAME = XFileUploader.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; /**??*/ private long mUploadFileSize; /**???*/ private int mUploadFileSizePerTime; /** ? */ private String mFilePath; /**?*/ private File mUploadFile; /**???id*/ private String mResponseId; /**??*/ private int mStartedPosition; /**???*/ private int mAlreadyUploadLength; /** ?? */ private String mServer; /** nativejs */ private XCallbackContext mCallbackCtx; /** ? */ private XIWebContext mWebContext; /** ?? */ private XFileTransferRecorder mFileTransferRecorder; /** ? */ private XFileTransferManager mFileTransferManager; public XFileUploader(String filePath, String server, XExtensionContext extensionContext, XIWebContext webContext, XFileTransferRecorder recorder, XFileTransferManager manager) { init(filePath, server, extensionContext, webContext, recorder, manager); } /** ? */ private void init(String filePath, String server, XExtensionContext extensionContext, XIWebContext webContext, XFileTransferRecorder recorder, XFileTransferManager manager) { mFilePath = filePath; mServer = server; mWebContext = webContext; mFileTransferRecorder = recorder; mFileTransferManager = manager; } @Override public void onSuccess() { mFileTransferRecorder.deleteUploadInfo(mFilePath); setState(INIT); mFileTransferManager.removeFileTranferTask(mWebContext.getApplication().getAppId(), 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()); } XExtensionResult result = new XExtensionResult(status, error); mCallbackCtx.sendExtensionResult(result); } // TODO:??? @Override public void onProgressUpdated(int completeSize, long totalSize) { JSONObject jsonObj = new JSONObject(); Status status = Status.PROGRESS_CHANGING; try { jsonObj.put("loaded", completeSize); jsonObj.put("total", totalSize); } catch (JSONException e) { status = Status.JSON_EXCEPTION; XLog.e(CLASS_NAME, e.getMessage()); } XExtensionResult result = new XExtensionResult(status, jsonObj); result.setKeepCallback(true); mCallbackCtx.sendExtensionResult(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(XCallbackContext 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() { XPathResolver pathResolver = new XPathResolver(mFilePath, mWebContext.getWorkSpace()); String absoluteFilePath = pathResolver.resolve(); File uploadFile = new File(absoluteFilePath); if (null != absoluteFilePath) { uploadFile = new File(absoluteFilePath); if (uploadFile.exists()) { return uploadFile; } } return null; } @Override public void pause() { setState(PAUSE); } }