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.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import com.polyvi.xface.app.XApplication; import com.polyvi.xface.extension.XCallbackContext; import com.polyvi.xface.extension.XExtensionContext; import com.polyvi.xface.extension.XExtensionResult; import com.polyvi.xface.util.XFileUtils; import com.polyvi.xface.util.XLog; public class XFileDownloader implements XIFileTransferListener, XIFileTransfer { private static final String CLASS_NAME = XFileDownloader.class.getSimpleName(); private static final int TIME_OUT_MILLISECOND = 5000; /** ??????? */ private static final int INIT = 1; private static final int DOWNLOADING = 2; private static final int PAUSE = 3; private int mState = INIT; private static final int CONNECTION_ERR = 3; /**????*/ 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 FILE_NOT_EXIST = 0; /**temp?*/ private static final String TEMP_FILE_SUFFIX = ".temp"; /**?*/ private static final int RETRY = 3; /**?1*/ private static final int RETRY_INTERVAL = 1000; /**?*/ private int mBufferSize; /** */ private String mUrl; /** */ private String mLocalFilePath; /** nativejs */ private XCallbackContext mCallbackCtx; /** ? */ private XApplication mApp; /** ? */ private XFileDownloadInfo mDownloadInfo; /** ?? */ private XFileTransferRecorder mFileTransferRecorder; /** ? */ private XFileTransferManager mFileTransferManager; private Context mContext; private CookieSyncManager mCookieSyncManager; public XFileDownloader(Context context, String url, String localFilePath, XExtensionContext extensionContext, XApplication app, XFileTransferRecorder recorder, XFileTransferManager manager) { init(context, url, localFilePath, extensionContext, app, recorder, manager); } /** ? */ private void init(Context context, String url, String localFilePath, XExtensionContext extensionContext, XApplication app, XFileTransferRecorder recorder, XFileTransferManager manager) { mUrl = url; mLocalFilePath = localFilePath; mApp = app; mFileTransferManager = manager; mFileTransferRecorder = recorder; mContext = context; mCookieSyncManager = CookieSyncManager.createInstance(mContext); } /** * ??(????? * ??????) */ private void initDownloadInfo() { int totalSize = 0; if (isFirst(mUrl)) { HttpURLConnection connection = null; try { URL url = new URL(mUrl); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(TIME_OUT_MILLISECOND); connection.setRequestMethod("GET"); System.getProperties().setProperty("http.nonProxyHosts", url.getHost()); //cookie? setCookieProperty(connection, mUrl); if (HttpURLConnection.HTTP_OK == connection.getResponseCode()) { totalSize = connection.getContentLength(); if (-1 != totalSize) { mDownloadInfo = new XFileDownloadInfo(totalSize, 0, mUrl); // ?mDownloadInfo?? mFileTransferRecorder.saveDownloadInfo(mDownloadInfo); } else { XLog.e(CLASS_NAME, "cannot get totalSize"); } // temp File file = new File(mLocalFilePath + TEMP_FILE_SUFFIX); if (file.exists()) { file.delete(); } } } catch (IOException e) { XLog.e(CLASS_NAME, e.getMessage()); } finally { if (null != connection) { connection.disconnect(); } } } else { // ?url? mDownloadInfo = mFileTransferRecorder.getDownloadInfo(mUrl); totalSize = mDownloadInfo.getTotalSize(); mDownloadInfo.setCompleteSize(getCompleteSize(mLocalFilePath + TEMP_FILE_SUFFIX)); } mBufferSize = getSingleTransferLength(totalSize); } /** * ? */ private boolean isFirst(String url) { return !mFileTransferRecorder.hasDownloadInfo(url); } @Override public void transfer(XCallbackContext callbackCtx) { initDownloadInfo(); if (mState == DOWNLOADING) { return; } mCallbackCtx = callbackCtx; if (null == mDownloadInfo) { onError(CONNECTION_ERR); } else { setState(DOWNLOADING); new Thread(new Runnable() { @Override public void run() { HttpURLConnection connection = null; RandomAccessFile randomAccessFile = null; InputStream is = null; int retry = RETRY; //TODO:????? do { int completeSize = mDownloadInfo.getCompleteSize(); try { URL url = new URL(mUrl); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(TIME_OUT_MILLISECOND); connection.setRequestMethod("GET"); // ?Rangebytes x-; connection.setRequestProperty("Range", "bytes=" + completeSize + "-"); //cookie setCookieProperty(connection, mUrl); // ?.temp randomAccessFile = new RandomAccessFile(mLocalFilePath + TEMP_FILE_SUFFIX, "rwd"); randomAccessFile.seek(completeSize); // ??? is = connection.getInputStream(); byte[] buffer = new byte[mBufferSize]; int length = -1; while ((length = is.read(buffer)) != -1) { try { randomAccessFile.write(buffer, 0, length); } catch (Exception e) { retry = -1; break; } completeSize += length; onProgressUpdated(completeSize, 0); if (PAUSE == mState) { break; } } if (mDownloadInfo.isDownloadCompleted()) { // ??.temp renameFile(mLocalFilePath + TEMP_FILE_SUFFIX, mLocalFilePath); onSuccess(); break; } } catch (IOException e) { if (retry <= 0) { onError(CONNECTION_ERR); XLog.e(CLASS_NAME, e.getMessage()); } // ,?1? try { Thread.sleep(RETRY_INTERVAL); } catch (InterruptedException ex) { XLog.e(CLASS_NAME, "sleep be interrupted", ex); } } finally { try { if (null != is) { is.close(); } if (null != randomAccessFile) { // new URL??randomAccessFilenull randomAccessFile.close(); } if (null != connection) { // new URL??connectionnull connection.disconnect(); } } catch (IOException e) { XLog.e(CLASS_NAME, e.getMessage()); } } } while ((DOWNLOADING == mState) && (0 < retry--)); } }).start(); } } private synchronized void setState(int state) { mState = state; } @Override public void pause() { setState(PAUSE); } @Override public void onSuccess() { mFileTransferRecorder.deleteDownloadInfo(mUrl); mFileTransferManager.removeFileTranferTask(mApp.getAppId(), mUrl); setState(INIT); JSONObject jsonObj = new JSONObject(); try { jsonObj = XFileUtils.getEntry(mApp.getWorkSpace(), new File(mLocalFilePath)); } catch (JSONException e) { XLog.e(CLASS_NAME, e.getMessage()); } mCallbackCtx.success(jsonObj); } @Override public void onError(int errorCode) { setState(INIT); String fullPath = null; String workspace = mApp.getWorkSpace(); if (mLocalFilePath.equals(workspace)) { fullPath = File.separator; } else { fullPath = mLocalFilePath.substring(workspace.length()); } JSONObject error = new JSONObject(); try { error.put("code", errorCode); error.put("source", mUrl); error.put("target", fullPath); } catch (JSONException e) { XLog.e(CLASS_NAME, e.getMessage()); } mCallbackCtx.error(error); } @Override public void onProgressUpdated(int completeSize, long totalSize) { mDownloadInfo.setCompleteSize(completeSize); JSONObject jsonObj = new JSONObject(); try { jsonObj.put("loaded", completeSize); jsonObj.put("total", mDownloadInfo.getTotalSize()); } catch (JSONException e) { XLog.e(CLASS_NAME, e.getMessage()); } XExtensionResult result = new XExtensionResult(XExtensionResult.Status.PROGRESS_CHANGING, jsonObj); result.setKeepCallback(true); mCallbackCtx.sendExtensionResult(result); } /** * ??<br/> * ????? <br/> * ??1k?2<br/> * ?1k-1M?10<br/> * ?1M-10M?20<br/> * ?10M?2M<br/> * */ private int getSingleTransferLength(int totalSize) { // ? int totalLength = totalSize; //?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; } } /** * ??? * * @param oldName:?? * @param newName:?? */ private void renameFile(String oldName, String newName) { File file = new File(oldName); file.renameTo(new File(newName)); } /** * ???-1 * * @param fileName:?? */ private int getCompleteSize(String fileName) { File file = new File(fileName); if (file.exists()) { return (int) file.length(); } return FILE_NOT_EXIST; } /** * connectionCookie * @param connection Http * @param domain cookie */ private void setCookieProperty(HttpURLConnection connection, String domain) { //Add cookie support mCookieSyncManager.startSync(); String cookie = CookieManager.getInstance().getCookie(domain); if (cookie != null) { connection.setRequestProperty("cookie", cookie); } } }