Java 在Android中进行分段文件上传的好方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20559435/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
A good approach to do multipart file upload in Android
提问by Allan Jiang
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. Here's what I have now:
我正在编写一段代码来执行多部分表单数据 POST 请求,在我的情况下,这只是将带有参数的图像上传到服务器。这是我现在所拥有的:
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 Clientto 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. 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 Volleylibrary, but it is not a handful tool when uploading large data files like image.
我每次都需要旋转一个新线程,因为最近的 Android 版本不允许程序在 UI 线程上发出 http 请求。但是,我真的反对像上面的代码一样旋转一个随机线程并让它失控。我曾尝试使用Google Volley库,但在上传像图像这样的大数据文件时,它不是少数工具。
I was wondering what I should do to make this call more manageable?
我想知道我应该怎么做才能使这个电话更易于管理?
===== UPDATE =====
===== 更新 =====
I switched to use AsyncTaskand it works OK for now. I will keep this question open to see if any one has better approach.
我改用AsyncTask并且现在工作正常。我会保留这个问题,看看是否有人有更好的方法。
回答by Anup Cowkur
HTTP has always been a pain point in Android. Fortunately, we have many great libraries that take care of all the hard parts.
HTTP 一直是 Android 的痛点。幸运的是,我们有许多很棒的库来处理所有困难的部分。
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.
它还可以做其他很酷的事情,比如智能缓存、当调用上下文超出范围时自动取消请求等。
P.S: Retrofitis another great library but I prefer Ion myself. Just a matter of preference.
PS:Retrofit是另一个很棒的库,但我自己更喜欢 Ion。只是喜好问题。
回答by Husain A.
1) Create Native Android Plugin for uploading Multiple files along with JSON Object. 2) Concept is Multipart file upload. 3) Create for offline mode sync. 4) Phonegap and Android Native Code.
1) 创建用于上传多个文件和 JSON 对象的原生 Android 插件。2)概念是多部分文件上传。3) 创建离线模式同步。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";
}
?>
回答by Maddy
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.) 实用程序类:
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);
}
}