Android上传图片(PHP服务器) – Norcy – 博客园

原理

Android客户端模拟一个HTTP的Post请求到服务器端,服务器端接收相应的Post请求后,返回响应信息给给客户端。

 

PHP服务器

 

&lt;?<span style="color: #000000;">php 
</span><span style="color: #008080;">  move_uploaded_file</span>(<span style="color: #800080;">$_FILES</span>['file']['tmp_name'], "./upload/".$_FILES["file"]["name"]); 
?&gt;

 

 

Android客户端

复制代码
<span style="color: #0000ff;">package</span><span style="color: #000000;"> com.example.uploadfile.app;</span>

import android.app.Activity;

import android.os.Bundle;

import android.os.Environment;

import android.os.StrictMode;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

import java.io.DataOutputStream;

import java.io.FileInputStream;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.URL;

public class MainActivity extends Activity

{

private String fileName = “image.jpg”; //报文中的文件名参数

private String path = Environment.getExternalStorageDirectory().getPath(); //Don’t use “/sdcard/” here

private String uploadFile = path + “/” + fileName; //待上传的文件路径

private String postUrl = “http://mycloudnote.sinaapp.com/upload.php”; //处理POST请求的页面

private TextView mText1;

private TextView mText2;

private Button mButton;

@Override

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//“文件路径:\n”+

mText1 = (TextView) findViewById(R.id.myText1);

mText1.setText(uploadFile);

//“上传网址:\n”+

mText2 = (TextView) findViewById(R.id.myText2);

mText2.setText(postUrl);

/* 设置mButton的onClick事件处理 */

mButton = (Button) findViewById(R.id.myButton);

mButton.setOnClickListener(new View.OnClickListener()

{

public void onClick(View v)

{

uploadFile();

}

});

}

/* 上传文件至Server的方法 */

private void uploadFile()

{

String end = “\r\n”;

String twoHyphens = “–“;

String boundary = “*****”;

try

{

URL url = new URL(postUrl);

HttpURLConnection con = (HttpURLConnection) url.openConnection();

/* Output to the connection. Default is false,

set to true because post method must write something to the connection */

con.setDoOutput(true);

/* Read from the connection. Default is true.*/

con.setDoInput(true);

/* Post cannot use caches */

con.setUseCaches(false);

/* Set the post method. Default is GET*/

con.setRequestMethod(“POST”);

/* 设置请求属性 */

con.setRequestProperty(“Connection”, “Keep-Alive”);

con.setRequestProperty(“Charset”, “UTF-8”);

con.setRequestProperty(“Content-Type”, “multipart/form-data;boundary=” + boundary);

/*设置StrictMode 否则HTTPURLConnection连接失败,因为这是在主进程中进行网络连接*/

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());

/* 设置DataOutputStream,getOutputStream中默认调用connect()*/

DataOutputStream ds = new DataOutputStream(con.getOutputStream()); //output to the connection

ds.writeBytes(twoHyphens + boundary + end);

ds.writeBytes(“Content-Disposition: form-data; ” +

“name=\”file\”;filename=\”” +

fileName + “\”” + end);

ds.writeBytes(end);

/* 取得文件的FileInputStream */

FileInputStream fStream = new FileInputStream(uploadFile);

/* 设置每次写入8192bytes */

int bufferSize = 8192;

byte[] buffer = new byte[bufferSize]; //8k

int length = -1;

/* 从文件读取数据至缓冲区 */

while ((length = fStream.read(buffer)) != -1)

{

/* 将资料写入DataOutputStream中 */

ds.write(buffer, 0, length);

}

ds.writeBytes(end);

ds.writeBytes(twoHyphens + boundary + twoHyphens + end);

/* 关闭流,写入的东西自动生成Http正文*/

fStream.close();

/* 关闭DataOutputStream */

ds.close();

/* 从返回的输入流读取响应信息 */

InputStream is = con.getInputStream(); //input from the connection 正式建立HTTP连接

int ch;

StringBuffer b = new StringBuffer();

while ((ch = is.read()) != -1)

{

b.append((char) ch);

}

/* 显示网页响应内容 */

Toast.makeText(MainActivity.this, b.toString().trim(), Toast.LENGTH_SHORT).show();//Post成功

} catch (Exception e)

{

/* 显示异常信息 */

Toast.makeText(MainActivity.this, “Fail:” + e, Toast.LENGTH_SHORT).show();//Post失败

}

}

}

复制代码

设置连接(HTTP头) -> 建立TCP连接 -> 设置HTTP正文 -> 建立HTTP连接(正式Post)-> 从返回的输入流读取响应信息

 

1.设置连接(HTTP头)

复制代码
URL url = <span style="color: #0000ff;">new</span><span style="color: #000000;"> URL(postUrl);</span>

HttpURLConnection con = (HttpURLConnection) url.openConnection();

/* Output to the connection. Default is false,

set to true because post method must write something to the connection */

con.setDoOutput(true);

/* Read from the connection. Default is true.*/

con.setDoInput(true);

/* Post cannot use caches */

con.setUseCaches(false);

/* Set the post method. Default is GET*/

con.setRequestMethod(“POST”);

/* 设置请求属性 */

con.setRequestProperty(“Connection”, “Keep-Alive”);

con.setRequestProperty(“Charset”, “UTF-8”);

con.setRequestProperty(“Content-Type”, “multipart/form-data;boundary=” + boundary);

复制代码

 

2.建立TCP连接

 

DataOutputStream ds = <span style="color: #0000ff;">new</span> DataOutputStream(con.getOutputStream());  <span style="color: #008000;">//</span><span style="color: #008000;">output to the connection</span>

 

con.getOutputStream()中会默认调用con.connect(),此时客户端与服务器建立的只是1个TCP连接而非HTTP。

HTTP请求=HTTP头+HTTP正文。

在connect()里面,会根据HttpURLConnection对象的配置值生成HTTP头,所以对con的一切配置都必须在connect()方法之前完成。

 

3.设置HTTP正文

正文通过DataOutputStream写入,只是写入内存而不会发送到网络中去,而是在流关闭时,根据写入的内容生成HTTP正文。

复制代码
ds.writeBytes(twoHyphens + boundary +<span style="color: #000000;"> end);</span>

ds.writeBytes(“Content-Disposition: form-data; ” +

“name=\”file\”;filename=\”” +

fileName + “\”” + end);

ds.writeBytes(end);

/* 取得文件的FileInputStream */

FileInputStream fStream = new FileInputStream(uploadFile);

/* 设置每次写入8192bytes */

int bufferSize = 8192;

byte[] buffer = new byte[bufferSize]; //8k

int length = -1;

/* 从文件读取数据至缓冲区 */

while ((length = fStream.read(buffer)) != -1)

{

/* 将资料写入DataOutputStream中 */

ds.write(buffer, 0, length);

}

ds.writeBytes(end);

ds.writeBytes(twoHyphens + boundary + twoHyphens + end);

/* 关闭流,写入的东西自动生成Http正文*/

fStream.close();

/* 关闭DataOutputStream */

ds.close();

复制代码

 

4.建立HTTP连接(正式Post)

至此,HTTP请求设置完毕,con.getInputStream()中会将请求(HTTP头+HTTP正文)发送到服务器,并返回一个输入流。所以在getInputStream()之前,HTTP正文部分一定要先设置好。

 

InputStream is = con.getInputStream();  <span style="color: #008000;">//</span><span style="color: #008000;">input from the connection 正式建立HTTP连接</span>

 

 

5.从返回的输入流读取响应信息

复制代码
<span style="color: #0000ff;">int</span><span style="color: #000000;"> ch;</span>

StringBuffer b = new StringBuffer();

while ((ch = is.read()) != -1)

{

b.append((char) ch);

}

/* 显示网页响应内容 */

Toast.makeText(MainActivity.this, b.toString().trim(), Toast.LENGTH_SHORT).show();//Post成功

复制代码

 

布局XML

两个Text和一个Button

复制代码
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">RelativeLayout </span><span style="color: #ff0000;">xmlns:android</span><span style="color: #0000ff;">="http://schemas.android.com/apk/res/android"</span>

xmlns:tools=”http://schemas.android.com/tools”

android:layout_width=”match_parent”

android:layout_height=”match_parent”

tools:context=”${packageName}.${activityClass}”>

<TextView

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:text=”New Text”

android:id=”@+id/myText1″

android:layout_above=”@+id/myText2″

android:layout_centerHorizontal=”true”

android:layout_marginBottom=”80dp”/>

<TextView

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:text=”New Text”

android:id=”@+id/myText2″

android:layout_centerVertical=”true”

android:layout_centerHorizontal=”true”/>

<Button

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:text=”Upload”

android:id=”@+id/myButton”

android:layout_marginTop=”80dp”

android:layout_below=”@+id/myText2″

android:layout_centerHorizontal=”true”/>

</RelativeLayout>

复制代码

 

AndroidManifest

添加网络权限、SD卡读写权限、挂载文件系统权限。

复制代码
<span style="color: #0000ff;">&lt;?</span><span style="color: #ff00ff;">xml version="1.0" encoding="utf-8"</span><span style="color: #0000ff;">?&gt;</span>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

package=”com.example.uploadfile.app” >

<application

android:allowBackup=”true”

android:icon=”@drawable/ic_launcher”

android:label=”@string/app_name”

android:theme=”@style/AppTheme” >

<activity

android:name=”com.example.uploadfile.app.MainActivity”

android:label=”@string/app_name” >

<intent-filter>

<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

</application>

<uses-sdk android:minSdkVersion=”9″ android:targetSdkVersion=”15″ />

<uses-permission android:name=”android.permission.INTERNET” />

<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

<uses-permission android:name=”android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>

</manifest>

复制代码

 

注意事项

1.由于Android不建议在主进程中进行网络访问,所以使用HttpURLConnection连接到服务端时抛出异常,加入以下语句即可。

 

StrictMode.setThreadPolicy(<span style="color: #0000ff;">new</span> StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());

 

2.获取SD卡路径时,请使用Environment.getExternalStorageDirectory().getPath();而不是”/sdcard/”

 

参考文章

HttpURLConnection学习

Android上传文件到Web服务器,PHP接收文件(一)

【Android开发那点破事】解决Andriod使用HttpURLConnection 失败问题

来源URL:http://www.cnblogs.com/chenyg32/p/3822639.html