在Android中进行多部分文件上传的好方法

时间:2013-12-13 05:03:30

标签: java android multithreading request androidhttpclient

我正在处理一段代码来执行多部分表单数据POST请求,在我的情况下,这只是将图像上传到带有参数的服务器。这就是我现在所拥有的:

我有一个触发多部分请求的按钮,在按钮OnClickListener中,我有这个代码来旋转一个新线程:

new Thread(new Runnable(){

@Override
public void run() {

    String photoUri = getPhotoUri();
    String url = getEndPointUrl();

    try {   

    NewPostRequest.postFile(url, photoUri, <Other Params...>);

    } catch (Exception e) {
        // Exception Handling           
    } 
}).start();

NewPostRequest.postFile只是使用 Apache Http客户端来发出请求,基本上如下所示:

HttpClient client = new DefaultHttpClient();

HttpPost post = new HttpPost(url);

MultipartEntityBuilder builder = MultipartEntityBuilder.create();    

builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

File file = new File(fileUri);

FileBody fb = new FileBody(file);

builder.addPart("file", fb);

builder.addTextBody("param", otherParam);

HttpEntity entity = builder.build();

post.setEntity(entity);

HttpResponse response = client.execute(post);      

我需要每次都旋转一个新线程,因为最近的Android版本不允许程序在UI线程上发出http请求。但是,我真的反对旋转一个随机线程,让它像上面的代码一样失控。我曾尝试使用Google Volley库,但在上传像图像这样的大型数据文件时,它并不是一种极少数的工具。

我想知道我应该做些什么来使这个电话更容易管理?

=====更新=====

我切换到使用 AsyncTask ,它现在可以正常工作了。我会保持这个问题,看看是否有人有更好的方法。

3 个答案:

答案 0 :(得分:10)

HTTP一直是Android的痛点。幸运的是,我们有许多伟大的库来处理所有困难的部分。

试用Ion

它允许您在后台线程中轻松执行多部分请求,并在请求完成时让您在主线程上获得回调。

当调用Context超出范围等时,它还会执行其他很酷的功能,如智能缓存,自动请求取消等。

P.S:Retrofit是另一个很棒的图书馆,但我更喜欢Ion。只是一个偏好问题。

答案 1 :(得分:3)

上述问题的解决方案与将.db(任何扩展名)文件上传到服务器有关: 以下是上传文件的步骤:

1: - new AsynUpload().execute();

2: -

class AsynUpload extends AsyncTask<Void,Integer,String>
        {
        String result="";
        ProgressDialog dialog=null;
        String iFileName = CONST.USER_NAME+".db";
        String lineEnd = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
        String Tag="fSnd";

        @Override
        protected void onPreExecute()
        {
        Log.i("AsynUpload is callinmg...", "calling");
        dialog=new ProgressDialog(Upload_Database.this);
        dialog.setMessage("File uploading...");
        dialog.setCancelable(false);
        dialog.show();
        }

        @Override
        protected String doInBackground(Void... params) {
        try 
        {
        UTILITIES.copyDBToSDCard();
        String selectedFilePath = "/data/data/com.DxS.androidSunTec.visioapp/databases/"+CONST.USER_NAME+".db";
        FileInputStream fstrm = new FileInputStream(selectedFilePath);
        URL connectURL = new URL(CUSTOM_URL.UPLOAD_URL+"Default.aspx");
        HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
        // Allow Inputs
        conn.setDoInput(true);
        // Allow Outputs
        conn.setDoOutput(true);
        // Don't use a cached copy.
        conn.setUseCaches(false);
        // Use a post method.
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);
        conn.setRequestProperty("FILE_NAME", ""+CONST.USER_NAME+".db");
        DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
        dos.writeBytes(twoHyphens + boundary + lineEnd);
        dos.writeBytes("Content-Disposition: form-data; name=\"title\""+ lineEnd);
        dos.writeBytes(lineEnd);
        dos.writeBytes(""+CONST.USER_NAME);
        dos.writeBytes(lineEnd);
        dos.writeBytes(twoHyphens + boundary + lineEnd);                    
        dos.writeBytes("Content-Disposition: form-data; name=\"description\""+ lineEnd);
        dos.writeBytes(lineEnd);
        dos.writeBytes(loc_code+"~"+user_code+"~"+fyid);
        dos.writeBytes(lineEnd);
        dos.writeBytes(twoHyphens + boundary + lineEnd);
        dos.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + iFileName +"\"" + lineEnd);
        dos.writeBytes(lineEnd);

        // create a buffer of maximum size
        int bytesAvailable = fstrm.available();

        int maxBufferSize = 1024;
        int bufferSize = Math.min(bytesAvailable, maxBufferSize);
        byte[ ] buffer = new byte[bufferSize];

        // read file and write it into form...
        int bytesRead = fstrm.read(buffer, 0, bufferSize);

            while (bytesRead > 0)
            {
            dos.write(buffer, 0, bufferSize);
            bytesAvailable = fstrm.available();
            bufferSize = Math.min(bytesAvailable,maxBufferSize);
            bytesRead = fstrm.read(buffer, 0,bufferSize);
            }
            dos.writeBytes(lineEnd);
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

            // close streams
            fstrm.close();
            // 103424
            dos.flush();
            InputStream is = conn.getInputStream();

            // retrieve the response from server
            int ch;

            StringBuffer b =new StringBuffer();
            while( ( ch = is.read() ) != -1 ){ b.append( (char)ch ); }
            String s=b.toString();
            Log.i("Response",s);
            dos.close();

            result="OK";
            }

            catch (MalformedURLException ex)
            {
            result = "MalformedURLException";
            Log.i(Tag, "URL error: " + ex.getMessage(), ex);
            }
            catch (IOException ioe)
            {
            result = "IOException";
            Log.i(Tag, "IO error: " + ioe.getMessage(), ioe);
            }
            catch(Exception e)
            {
            Log.e("Exception","Exception"+e.getMessage());
            result="FAILURE";
            }
            finally
            {
            if (result.equalsIgnoreCase("OK"))
            {
            File file = new File("/data/data/com.test.app/databases/"+CONST.USER_NAME+".db");
                if(file.exists())
                {
                file.delete();
                Log.i("uploading database file Deleted from sd card :", "deleted");
                }
            file = new File("/data/data/com.test.app/databases/"+DatabaseHelper.DB_NAME);
                if(file.exists())
                {
                file.delete();
                Log.i("Original database file Deleted from sd card :", "deleted");
                }

                MyActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                    // TODO Auto-generated method stub
                    AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
                    LayoutInflater inflater = getLayoutInflater();
                    View vw = inflater.inflate(R.layout.custom_title, null);
                    builder.setCustomTitle(vw);
                    builder.setMessage("File uploaded successfully!")
                    .setCancelable(false)
                    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                            finish();
                            }
                        });
                            builder.create();
                            builder.show();
                            }
                        });
                        }
                    else
                    {
                    MyActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                        AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
                        LayoutInflater inflater = getLayoutInflater();
                        View vw = inflater.inflate(R.layout.custom_title, null);
                    builder.setCustomTitle(vw);
                    builder.setMessage("File uploading failed, please try again!")
                    .setCancelable(false)
                    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                                dialog.cancel();
                                }
                            });
                                builder.create();
                                builder.show();
                                }
                            });
                            }
                              }
                            return result;
                        }   

                         @Override
                         protected void onProgressUpdate(Integer... values)
                         {
                             super.onProgressUpdate(values);
                         // dialog.incrementProgressBy(5);   
                        }
                        @Override
                        protected void onPostExecute(String result)
                        {
                            dialog.dismiss();

                            if (result.equalsIgnoreCase("OK"))
                            {
                            }
                            else
                            {
                            }
                        }
                      }

3。)UTILITIES类:

public static void copyDBToSDCard() {
        try {
            InputStream myInput = new FileInputStream("/data/data/com.DxS.androidSunTec.visioapp/databases/"+DatabaseHelper.DB_NAME);

            Log.i("sd card path: ", ""+Environment.getExternalStorageDirectory().getPath().toString());

        //    File file = new File(Environment.getExternalStorageDirectory().getPath()+"/"+CONST.USER_NAME+".db");
            File file = new File("/data/data/com.DxS.androidSunTec.visioapp/databases/"+CONST.USER_NAME+".db");
            if (!file.exists()){
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    Log.i("FO","File creation failed for " + file);
                }
            }

       //     OutputStream myOutput = new FileOutputStream(Environment.getExternalStorageDirectory().getPath()+"/"+CONST.USER_NAME+".db");
            OutputStream myOutput = new FileOutputStream("/data/data/com.DxS.androidSunTec.visioapp/databases/"+CONST.USER_NAME+".db");

            byte[] buffer = new byte[1024];
            int length;
            while ((length = myInput.read(buffer))>0){
                myOutput.write(buffer, 0, length);
            }

            //Close the streams
            myOutput.flush();
            myOutput.close();
            myInput.close();
            Log.i("FO","copied");

        } catch (Exception e) {
            Log.i("FO","exception="+e);
        }
}

答案 2 :(得分:-2)

1)创建原生Android插件,用于上传多个文件和JSON对象。 2)概念是Multipart文件上传。 3)创建离线模式同步。 4)Phonegap和Android Native Code。

Javascript调用原生Andorid代码

alert(window.FilesUpload.sendFiles(JSON.stringify(jsonObj)));

以下是Android插件(FilesUpload.java)

package com.yourpackagename.core;
import java.io.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.json.JSONArray;
import org.json.JSONObject;


public class FilesUpload
{
   public FilesUpload() {

   }

   public String sendFiles(String s) {       


       String responseBody = "";

       try
       {

          JSONObject jsonObject = new JSONObject(s);   

          int len_outer = jsonObject.getJSONArray("electricityExpenseManagement").getJSONObject(0).length();

          HttpClient httpclient = new DefaultHttpClient();

          HttpPost httppost = new HttpPost("http://yourservername.com/php_file_upload/file_upload2.php");

          MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);          

          StringBody elecExpObj = new StringBody(s);

          reqEntity.addPart("elecExpObj", elecExpObj);



           for(int i=0;i<len_outer;i++)
           {
               int bill_count = jsonObject.getJSONArray("electricityExpenseManagement").getJSONObject(i).getJSONArray("electricityExpenseBillInfoData").length();

               for(int j=0;j<bill_count;j++)
               {
                   String sourceFileUri = jsonObject.getJSONArray("electricityExpenseManagement").getJSONObject(0).getJSONArray("electricityExpenseBillInfoData").getJSONObject(j).getString("image_path");

                   String partName = jsonObject.getJSONArray("electricityExpenseManagement").getJSONObject(i).getJSONArray("electricityExpenseBillInfoData").getJSONObject(j).getString("image_base64_encode");                

                   FileBody bin = new FileBody(new File(sourceFileUri));

                   reqEntity.addPart(partName, bin);                   
               }
           }

           httppost.setEntity(reqEntity);          
           System.out.println("Requesting : " + httppost.getRequestLine());
           ResponseHandler<String> responseHandler = new BasicResponseHandler();
           responseBody = httpclient.execute(httppost, responseHandler);
           System.out.println("responseBody : " + responseBody);

           return responseBody;

      }
      catch (UnsupportedEncodingException e) {
         e.printStackTrace();
         return e.getMessage(); 
      }
      catch (ClientProtocolException e) {
         e.printStackTrace();
         return e.getMessage(); 
      }
      catch (IOException e) {
         e.printStackTrace();
         return e.getMessage(); 
      }
      catch(Exception e){
         e.printStackTrace();
         System.out.println("error");
         return e.getMessage();     
      }

   }
}

主要Android Java文件(定义插件类)

package com.yourpackagename.core;

import android.os.Bundle;
import org.apache.cordova.*;

public class Waterhealth extends DroidGap
{

    private FilesUpload f;

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        // Set by <content src="index.html" /> in config.xml

        super.init();

        f = new FilesUpload();
        appView.addJavascriptInterface(f, "FilesUpload");


        super.loadUrl(Config.getStartUrl());
        super.loadUrl("file:///android_asset/elect_exp_FS/index.html");
    }
}

用于上传文件的PHP脚本(Webservice)

<?php

if(isset($_REQUEST['elecExpObj']))
{        
        $obj= json_decode(stripslashes($_REQUEST['elecExpObj']));
        $len = count($obj->electricityExpenseManagement);
        $str = "";
        for($i=0;$i<$len;$i++)
        {
           $bill_count = count($obj->electricityExpenseManagement[$i]->electricityExpenseBillInfoData);
           for($j=0;$j<$bill_count;$j++)
           {
            $filePath = $obj->electricityExpenseManagement[$i]->electricityExpenseBillInfoData[$j]->image_base64_encode;


            if($_FILES[$filePath]['name'])
            {
              if(!$_FILES[$filePath]['error'])
          {
                 $new_file_name = $filePath . rand() . ".jpg"; //rename file    
                 move_uploaded_file($_FILES[$filePath]['tmp_name'], 'uploads/'.$new_file_name);
         $str .= 'Congratulations!  Your file was accepted.';                 
              }
            }
           }
        }
    echo $str;
}
else{
    echo "fail";
}
?>