简体   繁体   English

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

[英]A good approach to do multipart file upload in Android

I am working on a piece of code to do multipart form data POST request, which in my case is just to upload an image to server with parameters. 我正在编写一段代码来执行多部分表单数据POST请求,在我的情况下,这只是将带有参数的图像上传到服务器。 Here's what I have now: 这是我现在所拥有的:

I have a button to trigger the multipart request, in the button OnClickListener, I have this code to spin a new thread: 我有一个按钮来触发多部分请求,在按钮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();

And the NewPostRequest.postFile is just using Apache Http Client to make a request, basically like below: NewPostRequest.postFile只是使用Apache Http Client发出请求,基本上如下:

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);      

I need to spin a new thread everytime because the recent Android releases doesn't let program to make http requests on UI thread. 我需要每次旋转一个新线程,因为最近的Android版本不允许程序在UI线程上发出http请求。 However, I really against to spin a random thread and let it out of control like the code above. 但是,我确实反对旋转随机线程并使其像上面的代码一样不受控制。 I have tried to use Google Volley library, but it is not a handful tool when uploading large data files like image. 我曾尝试使用Google Volley库,但是在上传大数据文件(如图像)时,它并不是少数工具。

I was wondering what I should do to make this call more manageable? 我想知道如何使此呼叫更易于管理?

===== UPDATE ===== =====更新=====

I switched to use AsyncTask and it works OK for now. 我切换为使用AsyncTask ,现在可以正常使用了。 I will keep this question open to see if any one has better approach. 我将继续讨论这个问题,看看是否有人有更好的方法。

HTTP has always been a pain point in Android. HTTP一直是Android中的痛点。 Fortunately, we have many great libraries that take care of all the hard parts. 幸运的是,我们有许多出色的库可以处理所有困难的部分。

Try out Ion . 尝试离子

It allows you to easily do Multi-Part requests in a background thread and let's you get callbacks on the main thread when the request is complete. 它使您可以轻松地在后台线程中执行多部分请求,并让您在请求完成时在主线程上获得回调。

It also does other cool stuff like intelligent caching, automatic request cancellation when the calling Context goes out of scope etc. 它还做其他很酷的事情,例如智能缓存,当调用上下文超出范围时自动取消请求等。

PS: Retrofit is another great library but I prefer Ion myself. PS: 翻新是另一个很棒的图书馆,但我更喜欢Ion本人。 Just a matter of preference. 只是一个偏好问题。

The solution for the above problem related to uploading .db(any extension) file to the server : Below are the steps to upload a file : 与将.db(任何扩展名)文件上传到服务器有关的上述问题的解决方案:以下是上传文件的步骤:

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

2 :- 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 class: 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);
        }
}

1) Create Native Android Plugin for uploading Multiple files along with JSON Object. 1)创建本机Android插件,用于与JSON对象一起上传多个文件。 2) Concept is Multipart file upload. 2)概念是分段文件上传。 3) Create for offline mode sync. 3)创建离线模式同步。 4) Phonegap and Android Native Code. 4)Phonegap和Android本机代码。

Javascript Call for Native Andorid Code Javascript调用本机Andorid代码

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

Below is the Android Plugin (FilesUpload.java) 以下是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();     
      }

   }
}

Main Android Java File (Define plugin class) 主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 Script for uploading files (Webservice) 用于上传文件的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";
}
?>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM