原理

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

PHP服务器

<?php 
  move_uploaded_file($_FILES['file']['tmp_name'], "./upload/".$_FILES["file"]["name"]);
?>

Android客户端

package com.example.uploadfile.app;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失败        }    }}
View Code

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

1.设置连接(HTTP头)

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

2.建立TCP连接

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

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

HTTP请求=HTTP头+HTTP正文。

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

3.设置HTTP正文

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

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];   //8kint 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();  //input from the connection 正式建立HTTP连接

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

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成功

布局XML

两个Text和一个Button

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    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>
View Code

AndroidManifest

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

<?xml version="1.0" encoding="utf-8"?><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>
View Code

注意事项

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

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

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

参考文章

HttpURLConnection学习

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

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

更多相关文章

  1. Android Studio中的build.gradle文件解析
  2. Github android客户端源代码分析之一:环境搭建
  3. Android利用DownloadManager实现文件下载
  4. Android7.0文件读写(获取拍照结果)笔记
  5. 2010-02-27 传智播客—Android(二)数据存储和访问 之文件
  6. 通过修改hosts文件成功更新Android sdk .

随机推荐

  1. android的单元测试
  2. 图表(APAndroidChart)
  3. Android通知Notification学习 及 无法通
  4. android开发环境配置
  5. 深入了解Android
  6. Android下的任务和Activity栈
  7. android 判断当前application 是在前台还
  8. android > ListView -- SimpleAdapter
  9. Android Studio 3.0找不到Android Device
  10. android 5.1 usb调试默认关闭设置方法