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.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaWebView; import org.apache.cordova.PluginResult; import org.apache.cordova.file.FileUtils; import org.json.JSONException; import org.json.JSONObject; import com.polyvi.xface.util.XLog; import android.content.Context; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; public class FileDownloader implements IFileTransferListener, IFileTransfer { private static final String CLASS_NAME = FileDownloader.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 FILE_NOT_FOUND_ERR = 1; 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 CallbackContext mCallbackCtx; /** ? */ private FileDownloadInfo mDownloadInfo; /** ?? */ private FileTransferRecorder mFileTransferRecorder; /** ? */ private FileTransferManager mFileTransferManager; private Context mContext; private CookieSyncManager mCookieSyncManager; private CordovaWebView mWebView; public FileDownloader(Context context, String url, String localFilePath, FileTransferRecorder recorder, FileTransferManager manager, CordovaWebView webView) { this.mUrl = url; this.mLocalFilePath = localFilePath; this.mFileTransferManager = manager; this.mFileTransferRecorder = recorder; this.mContext = context; this.mCookieSyncManager = CookieSyncManager.createInstance(this.mContext); mWebView = webView; } /** * ??(????? * ??????) */ 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 FileDownloadInfo(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(CallbackContext 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, mDownloadInfo.getTotalSize()); mDownloadInfo.setCompleteSize(completeSize); if (PAUSE == mState) { break; } } if (mDownloadInfo.isDownloadCompleted()) { // ??.temp renameFile(mLocalFilePath + TEMP_FILE_SUFFIX, mLocalFilePath); onSuccess(); break; } } catch (FileNotFoundException e) { onError(FILE_NOT_FOUND_ERR); XLog.e(CLASS_NAME, e.getMessage()); } 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(mUrl); setState(INIT); JSONObject jsonObj = new JSONObject(); try { File file = new File(mLocalFilePath); // create FileEntry object FileUtils filePlugin = (FileUtils) mWebView.pluginManager.getPlugin("File"); if (filePlugin != null) { jsonObj = filePlugin.getEntryForFile(file); jsonObj.put("start", 0); jsonObj.put("end", file.length()); jsonObj.put("status", "finished"); } } catch (JSONException e) { XLog.e(CLASS_NAME, e.getMessage()); } mCallbackCtx.success(jsonObj); } @Override public void onError(int errorCode) { setState(INIT); String fullPath = null; fullPath = mLocalFilePath; 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 { File file = new File(mLocalFilePath); FileUtils filePlugin = (FileUtils) mWebView.pluginManager.getPlugin("File"); if (filePlugin != null) { jsonObj = filePlugin.getEntryForFile(file); jsonObj.put("loaded", completeSize); jsonObj.put("total", totalSize); jsonObj.put("status", "unfinished"); } } catch (JSONException e) { XLog.e(CLASS_NAME, e.getMessage()); } PluginResult result = new PluginResult(PluginResult.Status.OK, jsonObj); result.setKeepCallback(true); mCallbackCtx.sendPluginResult(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); } } }