android實現檔案的斷點上傳

來源:互聯網
上載者:User

在android開發過程中,檔案上傳非常常見。但是檔案的斷點續傳就很少見了。因為android都是通過http協議請求伺服器的,http本身不支援檔案的斷點上傳。但是http支援檔案的斷點下載,可以通過http檔案斷點下載的原理來實現檔案的斷點上傳,檔案的斷點下載比較簡單,主要步驟如下
(1)開啟服務,通過服務後台下載檔案
(2)conn.getContentLength();擷取要下載檔案的長度,建立對應大小的檔案
(3) 通過range欄位來設定我們要下載的檔案的開始位元組和結束位元組。http.setRequestProperty(“Range”, “bytes=” +
startPos + “-” + endPos) //注意格式中間加“-”
(4)通過sqllite儲存我們下載的進度,以便在重新開始任務的時候斷點下載
(5)通過廣播將下載的進度回調到主線程,更新進度條
(6)利用RandomAccessFile的seek方法,跳過已下載的位元組數來將下載的位元組寫入檔案
通過這六步就可以實現簡單的斷點續傳,當然單線程下載效能比較差。在最後的最佳化過程中還要加入多線程下載來提高我們的下載效率。ok檔案的斷線下載就說到這裡,這不是我們要談論的重點,接下來才是核心內容。
檔案的上傳對於android來說是非常耗時的操作,因此不能再主線程中進行。還是要利用服務來完成。但是上傳的時候並沒有range欄位來傳入我們要上傳的開始位元組和結束位元組。這應該怎樣做呢,這就要求對於檔案上傳,我們的伺服器要做出相應的改變了。在上傳檔案的時候,將檔案的大小的名稱和大小作為參數上傳到伺服器,這樣伺服器就記錄了要上傳的檔案大小。在上傳過程中服務端要對已上傳的位元組數進行記錄。在斷點時,移動端不用記錄檔案的已上傳的大小,而是首先去請求伺服器得到已上傳的位元組數,移動端做的只是跳過相應的位元組數來讀取檔案即可。在這裡有必要說一下android通過http請求時如何與伺服器互動的。移動端得到與伺服器響應的連結後,通過流來進行資料的傳遞,在移動端讀取檔案的時候,並沒有全部緩衝到流中,而是在android端做了緩衝,而android的記憶體有限,在上傳大檔案時就會記憶體溢出。在往流裡面寫入資料的時候,伺服器也不能及時得到流內的資料,而是當移動端提交以後伺服器才能做出response,移動端就是依據response判斷檔案上傳是否成功。這樣就會出現一個問題,android要即時提交檔案,負責記憶體溢出導致程勳崩潰。在上傳檔案時必須先對檔案校正,以免檔案重複上傳。對於重複上傳的檔案,服務端只要將以上傳的檔案與使用者關聯就可以了。檔案的校正當然是md5校正了,md5校正也是耗時操作,因此也要多執行緒。在進度條的更新上還是使用廣播來更新就可以了,當然也可以用handler來更新進度條。這個全憑個人喜好。這樣檔案的斷點上傳思路就有了。
(1)開啟服務,通過服務後台檢驗檔案和上傳檔案
(2)在檢驗檔案的response中得到檔案的已上傳位元組數
(3) 利用RandomAccessFile的seek方法,跳過已上傳的位元組數來讀取檔案
(4)多檔案上傳時通過sqllite儲存檔案的狀態,對於已上傳的檔案將狀態值設定為已上傳或者在資料庫刪除
(5)通過廣播將下載的進度回調到主線程,更新進度條
下面是檔案斷點上傳的主要代碼

  @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.tv_shangchuan:            //開始上傳時將選中的檔案路徑通過intent傳給服務                list_filePath.clear();                //將檔案的路徑傳入service                int file_count = map_check_file.size();//                L.i("-----count"+file_count);                if(file_count==0){                    T.showShort(this,"空檔案夾不能上傳");                    return;                }                loading_dialog1.show();                for(Map.Entry<String, File> entry: map_check_file.entrySet()){                    File value = entry.getValue();                    list_filePath.add(value.getAbsolutePath());                }                Intent intent_service = new Intent();                intent_service.setAction(MainActivity.ACTION_START);                intent_service.setPackage(getPackageName());                intent_service.putExtra("file_list", (Serializable) list_filePath);                getApplication().startService(intent_service);                break;            case R.id.tv_lixian:                if (map_check_file.size() != 0) {                    for (Map.Entry<String, File> entry : map_check_file.entrySet()) {                        //將未上傳的檔案加入資料庫                        fileDaoImp.insertData(entry.getValue().getAbsolutePath());                    }                    Fragment fragmentById = manager.findFragmentById(R.id.fl_director_activity);                    FileFragment fileFragment = (FileFragment) fragmentById;                    if (fileFragment != null) {                        sendMessageToFragment(fileFragment);                    }                    T.showShort(this, "已加入離線");                    rl_director_bottom.setVisibility(View.GONE);                } else {                    T.showShort(this,"空檔案夾不能加入離線");                }                break;        }    }

因為在項目中有離線功能,就一併貼出來吧,離線的實現也較為簡單。就是當使用者點擊離線的時候,將離線檔案的路徑進入資料庫,然後通過廣播判斷該網路狀態,當wifi條件下,讀取資料庫中未下載檔案然後開啟服務下載。
服務的代碼如下:

public class DownLoadService extends Service implements APICallBack {    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case 1:                    downLoadTask = new DownLoadTask(DownLoadService.this);                    downLoadTask.download();                    break;            }        }    };    public static final String RECEIVI = "UPDATEPROGRESS";    //下載檔案的線程    private DownLoadTask downLoadTask = null;    //檔案斷點上傳的資料庫管理類    FileDaoImp fileDaoImp = new FileDaoImp(DownLoadService.this);    boolean isFirst = true;    List<String> list_file_path = new ArrayList<>();    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        if (intent != null) {            if (MainActivity.ACTION_START.equals(intent.getAction())) {                downLoadTask.isPause = false;                String loading_shangchuan = intent.getStringExtra("loading_shangchuan");                if (loading_shangchuan != null && loading_shangchuan.equals("loading_shangchuan")) {                    isFirst = false;                    new InitThread().start();                    return super.onStartCommand(intent, flags, startId);                }                list_file_path = (List<String>) intent.getSerializableExtra("file_list");                isFirst = true;                Log.i("main", "--------list---Service--------------" + list_file_path.size());                //初始化線程                new InitThread().start();            } else if (MainActivity.ACTION_STOP.equals(intent.getAction())) {                if (downLoadTask != null) {                    downLoadTask.isPause = true;                    downLoadTask = null;                }            } else if (MainActivity.ACTION_CANCEL.equals(intent.getAction())) {                downLoadTask.isPause = true;                downLoadTask = null;                fileDaoImp.deletDateFileTask();                fileDaoImp.deleteFileUrl();            }        }//        START_NO_STICKY//         START_STICKY   預設調用        return super.onStartCommand(intent, flags, startId);    }//初始話檔案線程    class InitThread extends Thread {        @Override        public void run() {            if (isFirst) {                for (int i = 0; i < list_file_path.size(); i++) {                    File file = new File(list_file_path.get(i));//                    L.i("-------file-------------" + file.length());                    FileInfo fileInfo2 = null;                    try {                        if (!file.isDirectory()) {                            //將選中的檔案存入資料庫                            fileInfo2 = new FileInfo(2, file.getAbsolutePath(), file.getName(), file.length(), 0, MD5Util.getFileMD5String(file));                            fileDaoImp.insertFileUrl(fileInfo2.getUrl(), fileInfo2.getLength(), fileInfo2.getMd5(), fileInfo2.getFileName());                        }                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }            handler.obtainMessage(1).sendToTarget();        }    }}

//檔案上傳線程,將檔案按照5M分區上傳。下面也給出了android如何不再本機快取的方法。

/** * Created by zhoukai on 2016/5/3. * //                    int fixedLength = (int) fStream.getChannel().size(); * //                    已知輸出資料流的長度用setFixedLengthStreamingMode() * //                    位置輸出資料流的長度用setChunkedStreamingMode() * //                    con.setChunkedStreamingMode(塊的大小); * //                    如果沒有用到以上兩種方式,則會在本機快取後一次輸出,那麼當向輸出資料流寫入超過40M的大檔案時會導致OutOfMemory * //設定固定流的大小 * //                    con.setFixedLengthStreamingMode(fixedLength); * //                    con.setFixedLengthStreamingMode(1024 * 1024*20); */public class DownLoadTask {    private Context context;    private FileDaoImp fileDaoImp;    public static boolean isPause = false;    private long file_sum = 0;    String isExistUrl = "http://123.56.15.30:8080/upload/isExistFile";    String actionUrl = "http://123.56.15.30:8080/upload/uploadFile";    private int finishedLength;    public DownLoadTask(Context context) {        this.context = context;        fileDaoImp = new FileDaoImp(context);    }    public void download() {        new DownThread().start();    }    class DownThread extends Thread {        private double load_lenth = 0;        String end = "\r\n";        String twoHyphens = "--";        String boundary = "*****";        @Override        public void run() {            //未上傳的檔案            List<FileInfo> list = fileDaoImp.queryFileByState();            Log.i("main", "--------list--資料庫---------------" + list.size());            int sum_filelength = (int) fileDaoImp.getLengthByState(0);            if (list.size() == 0) {                return;            }            Intent intent = new Intent();            intent.setAction(DownLoadService.RECEIVI);            int nSplitter_length = 1024 * 1024 * 5;            for (int i = 0; i < list.size(); i++) {                int file_length = (int) list.get(i).getLength();                int count = file_length / nSplitter_length + 1;//                L.i("-------------------md5------------" + list.get(i).getMd5());//                L.i("------------------fileName------------" + list.get(i).getFileName());//---------------------驗證檔案--------------------------------------------------                URL realurl = null;                InputStream in = null;                HttpURLConnection conn = null;                try {                    realurl = new URL(isExistUrl);                    conn = (HttpURLConnection) realurl.openConnection();                    conn.setRequestProperty("accept", "*/*");                    conn.setRequestProperty("connection", "Keep-Alive");                    conn.setRequestProperty("user-agent",                            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");                    conn.setRequestMethod("POST");                    conn.setChunkedStreamingMode(1024 * 1024 * 10);                    //無窮大逾時                    conn.setReadTimeout(0);                    conn.setConnectTimeout(0);                    conn.setDoInput(true);                    conn.setDoOutput(true);                    PrintWriter pw = new PrintWriter(conn.getOutputStream());                    pw.print("userId=" + AppUtils.getUserName(context) + "&md5=" + list.get(i).getMd5()                            + "&did=" + getDid() + "&name=" + list.get(i).getFileName() + "&size=" + list.get(i).getLength());                    Log.i("main", "-------------userId---------" + AppUtils.getUserName(context));//                    Log.i("main", "-------------md5---------" + list.get(i).getMd5());//                    Log.i("main", "-------------did---------" + getDid());//                    Log.i("main", "-------------name---------" + list.get(i).getFileName());//                    Log.i("main","-------------size---------"+list.get(i).getLength());                    pw.flush();                    pw.close();                       /* 取得Response內容 */                    in = conn.getInputStream();                    int ch;                    StringBuffer stringBuffer = new StringBuffer();                    while ((ch = in.read()) != -1) {                        stringBuffer.append((char) ch);                    }                    String json = stringBuffer.toString();                    JSONObject jsonObject = new JSONObject(json);                    boolean isSuccess = jsonObject.optBoolean("success");                    if (isSuccess) {                        int lengths = jsonObject.optJSONObject("info").optJSONObject("file").optInt("length");                        finishedLength = lengths;                        if (finishedLength == list.get(i).getLength()) {                            fileDaoImp.deleteFilebyMd5(list.get(i).getMd5());                            fileDaoImp.deleteFilebyPath(list.get(i).getUrl());                            if (i == list.size() - 1) {                                intent.putExtra("progress", (load_lenth * 100 / ((double) sum_filelength)));                                intent.putExtra("state", "success");                                context.sendBroadcast(intent);                            }                            continue;                        }                        Log.i("main", "-----length_finished------" + finishedLength);                    }                } catch (Exception eio) {                    Log.i("main", "-----Exception------" + eio.toString());                }                //---------------------上傳檔案--------------------------------------------------                for (int j = 0; j < count; j++) {                    try {                        File file = new File(list.get(i).getUrl());                        URL url = new URL(actionUrl);                        HttpURLConnection con = (HttpURLConnection) url.openConnection();                        con.setChunkedStreamingMode(1024 * 1024 * 10);                        //無窮大逾時                        con.setReadTimeout(0);                        con.setConnectTimeout(0);            /* 允許Input、Output,不使用Cache */                        con.setDoInput(true);                        con.setDoOutput(true);                        con.setUseCaches(false);            /* 設定傳送的method=POST */                        con.setRequestMethod("POST");            /* setRequestProperty */                        con.setRequestProperty("Connection", "Keep-Alive");//建立長串連                        con.setRequestProperty("Charset", "UTF-8");        //編碼格式                        con.setRequestProperty("Content-Type",                                "multipart/form-data;boundary=" + boundary);//表單提交檔案                        DataOutputStream ds = new DataOutputStream(con.getOutputStream());                        //添加參數                        StringBuffer sb = new StringBuffer();                        Map<String, String> params_map = new HashMap<>();                        params_map.put("nSplitter", "3");                        params_map.put("md5", list.get(i).getMd5());                        params_map.put("dId", getDid());                        params_map.put("userId", AppUtils.getUserName(context));                        params_map.put("name", file.getName());                        params_map.put("from", finishedLength + "");                        Log.i("main", "-------------userId----上傳-----" + AppUtils.getUserName(context));                        if (finishedLength + nSplitter_length > file_length) {                            params_map.put("to", file_length + "");                        } else {                            params_map.put("to", (finishedLength + nSplitter_length) + "");                        }                        params_map.put("size", list.get(i).getLength() + "");                        //添加參數                        for (Map.Entry<String, String> entries : params_map.entrySet()) {                            sb.append(twoHyphens).append(boundary).append(end);//分界符                            sb.append("Content-Disposition: form-data; name=" + entries.getKey() + end);                            sb
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.