如何跟PHP Server溝通-檔案上傳(HttpUrlConnection)

如何跟PHP Server溝通-檔案上傳(HttpUrlConnection)

電子書

如果您需要更詳細的範例
更完整的說明可以取得電子書來學習這個單元
http://glarethink.weebly.com/php2android.html

其他相關章節

情境

Google 在 API 22 以後將 HttpClient deprecated
改成的是用 HttpUrlConnection
這個是從 API 1 就存在的類別
因此不會有不相容的問題
如果我們要上傳一個檔案
可以使用這個類別來進行實作

程式碼下載

你可以直接到 GitHub 觀看或下載完整的程式碼

程式碼說明

首先主畫面跟之前範例一樣, 宣告一個 Button 跟一個 TextView

<?xml version="1.0" encoding="utf-8"?>
<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" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
    <TextView
        android:id="@+id/msg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/upload"
        android:layout_below="@id/msg"
        android:text="Upload file"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>






接著在主程式寫

public class MainActivity extends AppCompatActivity {
    private Button upload;
    private TextView resMsg;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        upload = (Button) findViewById(R.id.upload);
        resMsg = (TextView) findViewById(R.id.msg);
        upload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        FileUpload mFileUpload = new FileUpload();
                        mFileUpload.setOnFileUploadListener(new FileUpload.OnFileUploadListener() {
                            @Override
                            public void onFileUploadSuccess(final String msg) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        resMsg.setText(msg);
                                    }
                                });

                            }

                            @Override
                            public void onFileUploadFail(final String msg) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        resMsg.setText(msg);
                                    }
                                });
                            }
                        });
                        mFileUpload.doFileUpload(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/butterfly.png");
                    }
                }).start();
            }
        });
    }

}

這邊稍微解釋一下, 一開始我們按下Button會去將圖片上傳到server,
由於這邊跟畫面沒有關係, 網路部分又會是屬於長任務的部分,
因此我們切出一個Thread讓它在背景執行,
對於Thread不熟悉的可以參考
執行緒套餐

所以程式碼就會變成

new Thread(new Runnable() {
                    @Override
                    public void run() {
                        FileUpload mFileUpload = new FileUpload();                       mFileUpload.doFileUpload(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/butterfly.png");
                    }
                }).start();

這邊可以看到我們從手機內的/DCIM/下面上傳一張butterfly.png






可以透過模擬器將這張圖片塞到Environment.getExternalStorageDirectory()路徑下,
我的路徑是

/mnt/emulated/0/DCIM/






那麼就可以透過Thread在背景上傳了,
上傳該怎麼做呢?

先來看一下上傳的程式碼

public class FileUpload {
    private String mResponseMsg;
    private boolean isSucess;

    public interface OnFileUploadListener{
        void onFileUploadSuccess(String msg);
        void onFileUploadFail(String msg);
    }

    private OnFileUploadListener mOnFileUploadListener;

    public void setOnFileUploadListener(OnFileUploadListener listener){
        mOnFileUploadListener = listener;
    }

    public boolean isSucess() {
        return isSucess;
    }

    public FileUpload(){
        mResponseMsg = "";
        isSucess = false;
    }

    public void doFileUpload(String path) {
        HttpURLConnection conn = null;
        DataOutputStream dos = null;
        DataInputStream inStream = null;
        String existingFileName = path;
        String lineEnd = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
        int bytesRead, bytesAvailable, bufferSize;
        byte[] buffer;
        int maxBufferSize = 1 * 1024 * 1024;
        String urlString = "http://192.168.56.1/upload.php";

        try {
            //------------------ CLIENT REQUEST
            FileInputStream fileInputStream = new FileInputStream(new File(existingFileName));
            // open a URL connection to the Servlet
            URL url = new URL(urlString);
            // Open a HTTP connection to the URL
            conn = (HttpURLConnection) url.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);
            dos = new DataOutputStream(conn.getOutputStream());
            dos.writeBytes(twoHyphens + boundary + lineEnd);
            dos.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + existingFileName + "\"" + lineEnd);
            dos.writeBytes(lineEnd);
            // create a buffer of maximum size
            bytesAvailable = fileInputStream.available();
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            buffer = new byte[bufferSize];
            // read file and write it into form...
            bytesRead = fileInputStream.read(buffer, 0, bufferSize);

            while (bytesRead > 0) {

                dos.write(buffer, 0, bufferSize);
                bytesAvailable = fileInputStream.available();
                bufferSize = Math.min(bytesAvailable, maxBufferSize);
                bytesRead = fileInputStream.read(buffer, 0, bufferSize);

            }

            // send multipart form data necesssary after file data...
            dos.writeBytes(lineEnd);
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
            // close streams
            fileInputStream.close();
            dos.flush();
            dos.close();
            isSucess = true;
        } catch (MalformedURLException e){
            isSucess = false;
        } catch (IOException e) {
            isSucess = false;
        }

        try {
            inStream = new DataInputStream(conn.getInputStream());
            String str;
            while ((str = inStream.readLine()) != null) {
                mResponseMsg = str;
            }
            inStream.close();

        } catch (IOException e) {
            isSucess = false;
            mResponseMsg = e.getMessage();
        }
        if(mOnFileUploadListener != null) {
            if (isSucess) {
                mOnFileUploadListener.onFileUploadSuccess(mResponseMsg);
            } else{
                mOnFileUploadListener.onFileUploadFail(mResponseMsg);
            }
        }
    }
}

好長一串啊~
接著是Server的php

<?php
    $target_path = "img/";
    $target_path = $target_path . basename( $_FILES['uploadedfile']['name']); 

    if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
        echo "The file ".  basename( $_FILES['uploadedfile']['name'])." has been uploaded";
    } else{
        echo "There was an error uploading the file, please try again!";
        echo "filename: " .  basename( $_FILES['uploadedfile']['name']);
        echo "target_path: " .$target_path;
    }
?>

這邊是根據 StackOverflow 的文章所進行改寫的
核心部分就是利用 HttpUrlConnection 將 File 弄成 Stream 然後上傳
而外加的部分是

public interface OnFileUploadListener{
    void onFileUploadSuccess(String msg);
    void onFileUploadFail(String msg);
}

private OnFileUploadListener mOnFileUploadListener;

public void setOnFileUploadListener(OnFileUploadListener listener){
    mOnFileUploadListener = listener;
}

public boolean isSucess() {
    return isSucess;
}

建立監聽器來實作當 Server 上傳結束後通知我們的 TextView 來進行更新
對 Listener 不熟的可以參考

透過成功或失敗的參數來進行不同字串的傳送

if(mOnFileUploadListener != null) {
    if (isSucess) {
        mOnFileUploadListener.onFileUploadSuccess(mResponseMsg);
    } else{
        mOnFileUploadListener.onFileUploadFail(mResponseMsg);
    }
}

當我們按下去






到後台去看會發現檔案已經出現




如果關掉網路