Android網絡編程—同時上傳參數和文件到服務器

畢業兩年了,也工作兩年了,但是由於具體的工作內容原因,真正去按照老大的要求去寫一個程序,還是第一次。

這次就遇到了一個問題,從來沒有接觸過的Android網絡編程,需求是按照指定的三種方法,以POST方法,分別用HTTP和Socket上傳一個文件到服務器。


指定的三種方法爲:

第一種:形如 "http://host:port/xxx/xxx/xxx/param?param1=xxx&param2=xxx"的形式

Params:

      param1

      param2

Data:

      file:要上傳的文件

第二種:形如“http://host:port/xxx/xxx/xxx”的形式

Part1:

      param1

Part2:

      param2

Part3:

      file:要上傳的文件

第三種:形如“http://host:port/xxx/xxx/xxx”的形式

Part1:

      param1

Part2:

      param2

Part3:

      file:要上傳的文件

利用Socket方法來發送



三種方法,首先搞得我頭暈腦脹,糾結了整整一天,後來終於理順了一些,現在把這些方法陳列出來,大家來批評指正,有不對的地方及時通知我來進行修改,大家共勉。

第一種和第二種都是模擬HTTP協議來發送的,使用HttpURLConnection類來發送,第三種使用Socket方法來發送。


首先我定義了一個上傳工具類:UploadUtil.java,裏面只是定義了一個Static的服務器地址,INTERNAL_HOST。

代碼如下

public class UploadUtil {
    
    static String INTERNAL_HOST = "http://host:port/xxx/xxx/xxx/upload/";
    
}

這個主機是在第一種方法的地址中,位於“param?”前面的部分。




完成第一種方法:

第一種方法的“http://host:port/xxx/xxx/xxx/param?param1=xxx&param2=xxx”,其實已經給出了POST的參數,param1和param2,我爲了弄着方便,就直接寫死了參數鍵值。其實也可以在方法內部,通過傳入的參數,進行循環拼裝,最後只要能組出類似“http://host:port/xxx/xxx/xxx/param?param1=xxx&param2=xxx”的字符串就行,最後通過URL來生成一個url對象即可。

public static void queryParam(String fileName)
    {
        String BOUNDARY = "---------------------------7db1c523809b2";//數據分割線
        File file = new File(fileName);   // 要上傳的文件
        String host = INTERNAL_HOST + "param?param1=xxx¶m2=xxx"; // 這個字符串就是要上傳的帶參數的服務器地址
        
        
        try 
        {
            byte[] after = ("--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
            
            // 構造URL和Connection
            URL url = new URL(host);            
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            
            // 設置HTTP協議的頭屬性
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
            conn.setRequestProperty("Content-Length", String.valueOf(file.length()));
            conn.setRequestProperty("HOST", url.getHost());
            conn.setDoOutput(true);
            
            // 得到Connection的OutputStream流,準備寫數據
            OutputStream out = conn.getOutputStream();
            InputStream in = new FileInputStream(file);
            
            
            // 寫文件數據。因爲服務器地址已經帶有參數了,所以這裏只要直接寫入文件部分就可以了。
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) != -1)
            {
                out.write(buf, 0, len);
            }             

            // 數據結束標誌,整個HTTP報文就構造結束了。
            //out.write(after);

            in.close();
            out.close();
            
            Log.d("carter", "queryParam 返回碼爲: " + conn.getResponseCode());
            Log.d("carter", "queryParam 返回信息爲: " + conn.getResponseMessage());
            
        }
        catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

大概的步驟:

1.先構造好URL服務器地址

2.初始化一個URL對象,得到裏面的HttpURLConnection對象

3.設置connection對象的頭部信息,包括POST方法、HOST、Content-Type、Content-Length等屬性

4.寫入要上傳的文件內容




完成第二種方法:

第二種的方法,不想第一種方法那樣簡單,但基本語法結構都相似,因爲都是模仿HTTP協議的形式。

第二種方法,在服務器地址上,只提供了上傳的HOST和PATH,具體的參數沒有提供,所以要通過在HTTP報文中添加來實現。


public static void multiPart(String fileName)
    {
        String BOUNDARY = "---------------------------7db1c523809b2";//數據分割線
        File file = new File(fileName);   // 要上傳的文件
        
        // 構造param參數部分的數據內容,格式都是相同的,依次添加param1和param2
        StringBuilder sb = new StringBuilder();
        sb.append("--" + BOUNDARY + "\r\n");
        sb.append("Content-Disposition: form-data; name=\"param1\"" + "\r\n");
        sb.append("\r\n");
        sb.append("xxx" + "\r\n");

        sb.append("--" + BOUNDARY + "\r\n");
        sb.append("Content-Disposition: form-data; name=\"param2\"" + "\r\n");
        sb.append("\r\n");
        sb.append("xxx" + "\r\n");

				// 構造要上傳文件的前段參數內容,和普通參數一樣,在這些設置後就可以緊跟文件內容了。
        sb.append("--" + BOUNDARY + "\r\n");
        sb.append("Content-Disposition: form-data; name=\"data\"; filename=\"" + fileName + "\"" + "\r\n");
        sb.append("Content-Type: text/plain" + "\r\n");
        sb.append("\r\n");
        
        try 
        {
            byte[] before = sb.toString().getBytes("UTF-8");
            byte[] after = ("--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
            URL url = new URL(INTERNAL_HOST);
            
            // 得到HttpURLConnection對象,設置一些頭信息基本屬性
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
            conn.setRequestProperty("Content-Length", String.valueOf(before.length + file.length() + after.length));
            conn.setRequestProperty("HOST", url.getHost());
            conn.setDoOutput(true);
            
            OutputStream out = conn.getOutputStream();
            InputStream in = new FileInputStream(file);
            
            // 寫入參數信息
            out.write(before);

            // 寫入文件數據
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) != -1)
            {
                out.write(buf, 0, len);
            }             

            // 寫結束符,代表該HTTP組包完畢
            out.write(after);
            
            // 發送出去
            out.flush();

            // 關閉流
            in.close();
            out.close();
            
            Log.d("carter", "multipart 返回碼爲: " + conn.getResponseCode());
            Log.d("carter", "multipart 返回信息爲: " + conn.getResponseMessage());
        }
        catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

具體步驟爲:

1.分別把param1等參數通過格式組織起來

2.得到HttpURLConnection對象,設置一些基本頭屬性,其中的Content-Length是所有param和文件加上尾標誌的總長度。

3.發送outputStream。




完成第三種方法:

第三種方法和第二種方法很相似,只是使用了Socket對象,而不是HttpURLConnection方法。


		public static void socket(String fileName)
    {
        String BOUNDARY = "---------------------------7db1c523809b2";//數據分割線
        File file = new File(fileName);   // 要上傳的文件
        
        // 構造參數內容字符串
        StringBuilder textParam = new StringBuilder();
        textParam.append("--" + BOUNDARY + "\r\n");
        textParam.append("Content-Disposition: form-data; name=\"param1\"" + "\r\n");
        textParam.append("\r\n");
        textParam.append("xxx" + "\r\n");

        textParam.append("--" + BOUNDARY + "\r\n");
        textParam.append("Content-Disposition: form-data; name=\"param2\"" + "\r\n");
        textParam.append("\r\n");
        textParam.append("xxx" + "\r\n");
        
        // 構造文件內容字符串
        int fileDataLen = 0;    // 文件內容的長度
        StringBuilder fileParam = new StringBuilder();
        fileParam.append("--" + BOUNDARY + "\r\n");
        fileParam.append("Content-Disposition: form-data;name=\"data\";" + "filename=\"" + fileName + "\"" + "\r\n");
        fileParam.append("Content-Type: text/plain" + "\r\n\r\n");
        fileParam.append("\r\n");
        
        // 得到文件內容前的聲明信息長度
        fileDataLen += fileParam.length();
        // 得到聲明信息和文件內容總長度
        fileDataLen += file.length();
        
        // HTTP報文數據總長度
        int totalDataLen = 0;
        
        try 
        {
            byte[] textEntity = textParam.toString().getBytes();
            byte[] after = ("--" + BOUNDARY + "--\r\n").getBytes();
            // 得到報文內數據總長度,參數聲明信息+文件信息+報文尾標誌信息
            totalDataLen = textEntity.length + fileDataLen + after.length;
            
            // 得到Socket
            URL url = new URL(INTERNAL_HOST);            
            Socket socket = new Socket(url.getHost(), 80);
            socket.setSoTimeout(60000);	// 設置Socket超時時間
            
            // 得到輸出流,靠它去發送報文
            OutputStream out = socket.getOutputStream();
            
            // 設置請求方法
            String requestMethod = "POST " + url.getPath() + " HTTP/1.1\r\n";
            out.write(requestMethod.getBytes());
            
            // 設置接收類型
            String accept = "Accept: image/gif, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml," +
            		"application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, " +
            		"application/vnd.ms-powerpoint, application/msword, */*\r\n";
            out.write(accept.getBytes());
            
            // 設置語言
            String language = "Accept-Language: zh-CN\r\n";
            out.write(language.getBytes());
            
            // 設置內容類型
            String contentType = "Content-Type: multipart/form-data; boundary=" + BOUNDARY + "\r\n";
            out.write(contentType.getBytes());
            
            // 設置報文長度
            String contentLength = "Content-Length: " + totalDataLen + "\r\n";
            out.write(contentLength.getBytes());
            
            // 設置活動
            String alive = "Connection: Keep-Alive\r\n";
            out.write(alive.getBytes());
            
            // 設置主機
            String host = "Host: " + url.getHost() + ":80\r\n";
            out.write(host.getBytes());
            
            // 設置一個回車換行
            out.write("\r\n".getBytes());
            
            
            // 添加所有文本類型數據
            out.write(textEntity);
            
            
            // 添加所有文件類型數據
            StringBuilder fileEntity = new StringBuilder();
            fileEntity.append("--" + BOUNDARY + "\r\n");
            fileEntity.append("Content-Disposition: form-data;name=\"data\";" + "filename=\"" + fileName + "\"" + "\r\n");
            fileEntity.append("Content-Type: text/plain" + "\r\n\r\n");
            out.write(fileEntity.toString().getBytes());
            
            // 將文件內容寫入報文
            FileInputStream fis = new FileInputStream(file);    // 以後記得加null判斷
            byte[] buffer = new byte[1024];
            int len = 0;
            while( (len=fis.read(buffer))!=-1 )
            {
                out.write(buffer, 0, len);
            }
            fis.close();
            out.write("\r\n".getBytes());
            
            // 數據結束標誌,代表數據已經結束
            out.write(after);
            
            
            // 輸出處理結果
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line = "";
            while( (line=reader.readLine())!=null)
            {
                Log.d("carter", line);
            }
           
            // 發送報文
            out.flush();
            
            // 關閉流
            out.close();
            reader.close();
            socket.close();            
        }
        catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

具體步驟:

1.構造參數信息

2.構造文件信息

3.構造尾信息

4.通過URL和端口得到一個Socket對象

5.設置Socket的屬性

6.得到一個輸出流,將所有信息輸出

7.輸出傳送結果



至此,三種方法就都實現了,糾結的內容,但是需要積極的去實現。

以上方法,只要替換其中的某些參數信息,就可以完全直接複用了。

發佈了36 篇原創文章 · 獲贊 4 · 訪問量 48萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章