Android上傳文件到服務器中的簡單實例

jopen 12年前發布 | 84K 次閱讀 Android Android開發 移動開發

      最近一直在完成個任務,有關Android手機文件傳輸的,現在先做了一步,實現了手機可以上傳文件到pc端。

先簡單介紹一下吧,架設在電腦上的pc端,運行在Android手機上的客戶端,pc端用java語言編寫,客戶端這邊是結合c和

java的JNI來編寫的。為什么這么特殊呢~呵呵 ,完全是出于任務要求的需要啦!

      先上代碼吧! 這邊為了思路清晰點先上客戶端的代碼~順序由上至下~

package zeng.Glogo.learn;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

public class JniClient_File extends Activity {
    static{
        System.loadLibrary("FileOperation");
    }

 我自己建的包,還有需要的一些包~ static{}內的代碼為用jni編寫的靜態庫~

public String IPAddress="";
    public int PORT;

    private EditText editText1=null;
    private EditText editText2=null;
    private Spinner spinner=null;
    private Button send=null;
    private EditText editText3=null;
    private EditText editText4=null;
    private Button sure=null;
    private Button connect=null;   //重點1
    private Button disconnect=null; //重點2
    private Button exit=null;
    FileOperation fileOperation=new FileOperation();   //對文件進行操作的類 ,重點3
    private ProgressDialog progressdialog;

這些都很簡單吧~

private static final String file_Selected[]={
        "選擇您需要傳輸的文件","HelloJni.c","HelloNDK.c","HelloCDT.txt","HelloJava.java","Hello.txt","hellop.txt"
    };
    private static final String filePath[]={
        " ","/mnt/sdcard/HelloJni.c","/mnt/sdcard/HelloNDK.c","/mnt/sdcard/HelloCDT.txt","/mnt/sdcard/HelloJava.java",
        "/mnt/sdcard/Hello.txt","/mnt/sdcard/hellop.txt"
    };
    private ArrayAdapter<String> adapter;   //聲明一個適配器
    private List<String> fileNamesList;      //List容器,存放選擇的文件名

有ArrayAdapter和List,大家應該才出來這些都是為Spinner做準備的吧~

 /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        //根據控件的ID找到各個控件
        editText1=(EditText)findViewById(R.id.file_name);
        editText2=(EditText)findViewById(R.id.file_seletced);
        spinner=(Spinner)findViewById(R.id.spinner);
        send=(Button)findViewById(R.id.send);
        editText3=(EditText)findViewById(R.id.ip);
        editText4=(EditText)findViewById(R.id.port);
        sure=(Button)findViewById(R.id.sure);
        //progressbar=(ProgressBar)findViewById(R.id.progressBar);
        connect=(Button)findViewById(R.id.connect);
        disconnect=(Button)findViewById(R.id.disconnect);
        exit=(Button)findViewById(R.id.exit);

        //為容器List添加內容
        fileNamesList=new ArrayList<String>();
        for(int i=0;i<file_Selected.length;i++){
            fileNamesList.add(file_Selected[i]);
        }
        //適配器設置
        adapter=new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, file_Selected);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        //為Spinner添加適配器
        spinner.setAdapter(adapter);
        //為Spinner添加時間監聽
        spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> arg0, View arg1,
                    int arg2, long arg3) {
                // TODO Auto-generated method stub
                //arg2為點擊所選擇的選項
                //arg0為spinner設置顯示當前的選項
                if(arg2!=0){
                    editText1.setText(filePath[arg2]);
                    editText2.setText(file_Selected[arg2]);
                    arg0.setVisibility(View.VISIBLE);
                }else{
                    editText1.setText("");
                    editText2.setText("");
                    editText1.setHint(R.string.file_name_hint);
                    editText2.setHint(R.string.file_seletced_hint);
                    arg0.setVisibility(View.VISIBLE);
                }
            }
            @Override
            public void onNothingSelected(AdapterView<?> arg0) {
                // TODO Auto-generated method stub
                //這個方法暫時不知道有什么用處,等待google之~
            }
        });

 上面這些東東如果大家不了解的話去看一下有關Android入門的書,這些都會有的~

接下來的就是幾個按鈕的設定了~

 

//退出
        exit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                JniClient_File.this.finish();
            }
        });
        //確定IP和端口號
        sure.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                IPAddress=editText3.getText().toString();
                PORT=Integer.decode(editText4.getText().toString());
                editText3.setText("");
                editText4.setText("");
                editText3.setHint(IPAddress);
                String port=String.valueOf(PORT);  //EditText的類型為Editable。接收String類型,所以在這里必須轉換一下類型
                editText4.setHint(port);
                Toast toast=Toast.makeText(JniClient_File.this, 
                        "IP地址;"+IPAddress+"\n"+"端口號:"+PORT, Toast.LENGTH_LONG);
                toast.show();
            }
        });

        //建立連接
        connect.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String str1=fileOperation.connect(IPAddress,PORT);
                if(str1.endsWith("101")){
                    Toast toast=Toast.makeText(JniClient_File.this, str1+" 沒有建立連接", Toast.LENGTH_LONG);
                    toast.show();
                }
                else{
                    Toast toast=Toast.makeText(JniClient_File.this, str1+" 連接已建立", Toast.LENGTH_LONG);
                    toast.show();
                }
            }
        });
        //斷開連接
        disconnect.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String str2=fileOperation.disconnect();
                if(str2.endsWith("102")){
                    Toast toast=Toast.makeText(JniClient_File.this, str2+"  斷開異常",Toast.LENGTH_LONG);
                    toast.show();
                }else{
                    Toast toast=Toast.makeText(JniClient_File.this, str2+" 連接已斷開", Toast.LENGTH_LONG);
                    toast.show();
                }

            }
        });

 大家應該主要到了斷開disconnect和 連接connect的功能都是調用我用jni編寫的那個靜態庫(FileOperation)來實現的吧~并且還有相應的錯誤提示信息~接下來是最后一個按鈕send~

 

send.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String str3=editText1.getText().toString();   //文件路徑
                String str4=editText2.getText().toString()+"\r\n";   //文件名
                //String str4=editText2.getText().toString();
                 int total=fileOperation.fileOperatin(str3,str4);
                if(total<=0){
                    Toast toast=Toast.makeText(JniClient_File.this, "上傳文件不成功"+total, Toast.LENGTH_LONG);
                    toast.show();
                }
                else{
                    Toast toast=Toast.makeText(JniClient_File.this, "the total is"+total, Toast.LENGTH_LONG);
                    toast.show();
                    progressdialog=new ProgressDialog(JniClient_File.this);
                    progressdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                    progressdialog.setTitle("文件傳輸進度");
                    progressdialog.setMessage("~稍等一會哈~");
                    progressdialog.setIcon(R.drawable.android1);
                    progressdialog.setProgress(100);
                    progressdialog.setIndeterminate(false);
                    progressdialog.setCancelable(false);
                    progressdialog.show();
                    Log.d("DUBUG", "total is"+total);
                    new Thread(){
                        int count=0;
                        public void run() {
                            // TODO Auto-generated method stub
                            try{
                                while(count<100)
                                {
                                progressdialog.setProgress(count+=4);
                                Thread.sleep(100);
                                }
                                progressdialog.cancel();
                            }catch(InterruptedException e){
                                e.printStackTrace();
                            }
                        }

                    }.start();
                }
            }
        });
    }
}

這個很簡單吧~發送的東西交友jni編寫的靜待庫去做了~它返回獨到的字節數并Toast出來,這個便于我們統計嘛~還有一個progredialog。額·這個···美化一下哈~實際上沒什么用處滴~

好了客戶端java部分就到此為止了,下面是重頭戲之一,FileOperation.so啦!!

繼續上代碼,大家如果對JNI有不熟悉的話可以先去了解一下哈~

 

#include<sys/socket.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/wait.h>
#include<netinet/in.h>
#include "zeng_Glogo_learn_FileOperation.h"

#define MAXBUF 1024
#define FILEPATH 255
#define FILENAME 255
int sockfd;
unsigned char buffer[MAXBUF];
char *end;
unsigned char end_buf[29];
struct sockaddr_in client_addr;

jint Java_zeng_Glogo_learn_FileOperation_fileOperatin
  (JNIEnv *env, jobject thiz, jstring FilePath,jstring FileName)
{
    const char *filepath_buf=(*env)->GetStringUTFChars(env,FilePath,0);
    char filepath[FILEPATH];
    strcpy(filepath,filepath_buf);
    (*env)->ReleaseStringUTFChars(env,FilePath,filepath_buf);

    const char *filename_buf=(*env)->GetStringUTFChars(env,FileName,0);
    char filename[FILENAME];
    memset(filename,0,FILENAME);
    strncpy(filename,filename_buf,strlen(filename_buf));
    (*env)->ReleaseStringUTFChars(env,FileName,filename_buf);
    //開始讀取文件,并發送給服務端
    FILE *fp;
    fp=fopen(filepath,"rb");
    if(!fp)
    {
        return -1;
    }
    int file_name=send(sockfd,filename,strlen(filename),0);   //發送文件名
    if(file_name<0)
    {
        return -2;
    }
    //int file_block_length=0;
    int count=0;                                  //將文件分塊傳輸
    int ReadNum=0;
    int ReadSum=0;
    unsigned char LenBuffer[1];
    while(!feof(fp))  //讀取文件的內容到buffer中
    {
        ReadNum=fread(buffer,1,MAXBUF,fp);
        ReadSum+=ReadNum;
        if(ReadNum>0)
        {
            if(send(sockfd,buffer,ReadNum,0)==-1)
            {
                fclose(fp);
                return -3;
            }
            bzero(buffer,MAXBUF);
            count++;
        }
        else
        {
            fclose(fp);
            break;
        }
    }

    //bzero(buffer,MAXBUF);
    /*end="EndLessLimiteFromGlogoPassion";
    strcmp(end_buf,end);
    send(sockfd,end_buf,29,0);*/
    //send(sockfd,end_buf,strlen(end_buf),0);
    fclose(fp);
    return ReadSum;
}


jstring Java_zeng_Glogo_learn_FileOperation_connect
  (JNIEnv *env, jobject thiz, jstring IPAddress, jint PORT)
{
    //轉換String類型
    const char * ipaddress_buf=(*env)->GetStringUTFChars(env,IPAddress,0);
    char ipaddress[255];
    strcpy(ipaddress,ipaddress_buf);
    (*env)->ReleaseStringUTFChars(env,IPAddress,ipaddress_buf);
    int port=PORT;

    bzero(&client_addr,sizeof(client_addr));             //把一段內存區的內容全部設置為0

    /* AF_INET域
    struct sockaddr_in
    {
    short int             sin_family;   //AF_INET
    unsigned short int    sin_port;     //Port number
    struct in_addr{
     unsigned  long       s_addr        //Internet address
    }
    }*/
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
    {
        return (*env)->NewStringUTF(env,"Socket Error 101");
    }
    client_addr.sin_family=AF_INET;                     //internet協議族
    client_addr.sin_port=htons(port);                   //端口號
    /*也可以這么寫
    client_addr.sin_addr.s_addr=inet_addr(ipaddress);    //轉化IP地址  inet_addr和inet_aton的不同在于結果返回值的形式不同,
                                                         //inet_addr返回值為in_addr_t, inet_aton返回值為整形,但兩者的轉換的地址仍存放在straddr中
                                                          //in_addr_t inet_addr(const char* straddr)  ,   int inet_aton(const char* straddr,struct in_addr *addrp)
                                                        //另外,sin_addr.s_addr=htonl(INADDR_ANY)表示*/

    if(inet_aton(ipaddress,&client_addr.sin_addr)<0)
    {
        return (*env)->NewStringUTF(env,"inet_aton Error 101");
    }

    if(connect(sockfd,(struct sockaddr*)&client_addr,sizeof(client_addr))<0)
    {
        return (*env)->NewStringUTF(env,"Connect Error 101");
    }
    else
    {
        return (*env)->NewStringUTF(env,"Connec OK!");
    }

}

jstring Java_zeng_Glogo_learn_FileOperation_disconnect
  (JNIEnv *env, jobject thiz)
{
    close(sockfd);
    return (*env)->NewStringUTF(env,"Socket Close!");

}

大家應該看到了~這些都是Linux下C編程的一些簡單的東西,這里說明一下,在jint Java_zeng_Glogo_learn_FileOperation_fileOperatin函數中的count變量是沒什么用的,我懶得刪掉而已哈~

在發送文件這邊沒什么的,就是根據傳進來的文件路徑FilePath打開文件讀取內容,并發送文件名給服務端,然后就是在!fp的情況下一次一次的send而已。嗯~客戶端的就到此為止啦!!

下面的是服務端的啦~在這里我糾結了很久,后來終于發現問題,發送方發送的字節數是對的,但是接收方由于是java編寫的,所以傳過來的時候會涉及到基本數據類型的轉換問題,這是一個老問題了~但是嘛~基礎不夠扎實的我還是忽略了~在這里耽誤了很多時間,好了~上代碼吧~!

 

package learn;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;

public class JniServer_File implements Runnable{

    int PORT=8888;

    /**
     * @param args
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        try{
            System.out.println("      服務器開啟...");
            System.out.println("----  ----  ----  ----");
            ServerSocket serverSocket=new ServerSocket(PORT);
            while(true){
                Socket client=serverSocket.accept();
                System.out.println("      接收到客戶端請求...");
                System.out.println("----  ----  ----  ----");
                System.out.println("      打開輸入流。。");
                System.out.println("----  ----  ----  ----");

                BufferedInputStream filename=new BufferedInputStream(client.getInputStream());
                System.out.println("      正在讀取內容(文件名)...");
                System.out.println("----  ----  ----  ----");
                byte file_name[]=new byte[255];
                filename.read(file_name);
                String file_name_trans=new String(file_name);
                System.out.println("      讀取文件名完畢,文件名是"+new String(file_name));

                System.out.println("----  ----  ----  ----");
                try{
                if(file_name_trans!=""){
                    System.out.println("      開始創建文件.. "+file_name_trans);

                        String file="D:/Eclipse/test/HelloCDT.txt";        //文件的絕對路徑

                    File newFile=new File(file);               //創建文件對象
                      if(newFile.exists())
                      {
                          //檢查文件在當前路徑下是否存在
                          newFile.createNewFile();  
                      }


                      System.out.println("----  ----  ----  ----");
                     System.out.println("----  ----  ----  ----"); 

                      System.out.println("       打開文件輸出流,準備將讀取內容寫入相應文件");
                       BufferedOutputStream file_context_in_buf=new BufferedOutputStream(new FileOutputStream(file,false));
                      System.out.println("----  ----  ----  ----"); 
                      System.out.println("       正在將內容寫入文件...");

                      int count=0;                          //測試用的標志
                      byte[] file_context=new byte[1024];
                      while(filename.read(file_context)>0){                     //循環讀取文件內容,并寫入到相應的文件保存起來
                          count++;                           
                          System.out.println("    read times for "+count);

                          String end_buf_str=new String(file_context);
                          if(end_buf_str.contains("END")){
                              int len=end_buf_str.lastIndexOf("END");
                              String end_buf_str1=end_buf_str.substring(0, len+3);
                              byte end_buf_byte[]=end_buf_str1.getBytes();
                              file_context_in_buf.write(end_buf_byte);
                              System.out.println("    write times for "+count);
                              System.out.println("    This times is "+count);
                              System.out.println("----  ----  ----  ----"); 
                              break;
                          }

                          file_context_in_buf.write(file_context);
                          System.out.println("    write times for "+count);
                          System.out.println("    This times is "+count);
                          System.out.println("----  ----  ----  ----"); 
                      }

                      file_context_in_buf.flush();
                      System.out.println("    file_context_in_buf flush times for "+count);
                      System.out.println("----  ----  ----  ----"); 
                      System.out.println("       寫入完畢,請打開文件查看..."+count);
                      System.out.println("----  ----  ----  ----"); 
                      System.out.println("       關閉文件各種流...");
                      System.out.println("----  ----  ----  ----"); 
                      file_context_in_buf.close();         //先關閉外層的緩沖連接流
                      filename.close();
                      file_name_trans="";

                     }

                }
                catch(IOException e){
                    e.printStackTrace();
                    System.out.println(e.getMessage()+" ---1");
                }
                finally{
                    client.close(); //關閉socket
                      System.out.println("     關閉連接");
                }
            }
        }
        catch(Exception e){
            e.printStackTrace();
                System.out.println(e.getMessage()+" ---2");
            }
        }


    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread jniServer_File=new Thread(new JniServer_File());
        jniServer_File.start();

    }
}

熟悉java的同學應該清楚上面的代碼吧~比較特殊的是在循環接收客戶端send()過來的東西的時候,我這邊做了一點小偷懶,就是發送是.txt文件最后都是以END結尾的,這個給了我一個方便,就是我可以根據這個來判斷什么時候終止再往文件寫入內容。還有一點是,傳輸是以字節為單位來傳輸的,所以要用Strean來接收和存入,用字符流Reader和Writer都是不靠譜的!這里面還涉及到String和byte類型的轉化問題,我在這里也糾結過很久啦~呵呵 ,大家先別噴,我坦誠是我的基礎部夠扎實啦~

好了基本就是這樣子的! 這邊的上圖比較麻煩,所以沒圖沒真相···額好吧········大家這樣想的話也么辦法啦·不過本人已經試驗過啦~一個15014KB的文件還有一個396800KB的文件傳輸都是沒問題的,放在手機上測試也OK~

上圖不方便我這里貼一下man.xml的代碼讓大家都整個布局都有些了解吧~

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello"
        android:id="@+id/tv" />
    <TextView 
        android:layout_marginTop="15dp"
        android:layout_below="@id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/dir"
        android:text="@string/dir"/>
    <EditText 
        android:layout_width="260dp"
        android:layout_height="wrap_content"
        android:hint="@string/file_name_hint"
        android:id="@+id/file_name"
        android:layout_below="@id/tv"
        android:layout_toRightOf="@id/dir"/>
    <TextView 
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="@string/dir1"
        android:layout_below="@id/dir"
        android:id="@+id/dir1"
        android:layout_marginTop="25dp"/>
    <EditText 
        android:layout_width="260dp"
        android:layout_height="wrap_content"
        android:hint="@string/file_seletced_hint"
        android:id="@+id/file_seletced"
        android:layout_below="@id/file_name"
        android:layout_toRightOf="@id/dir1"/>
    <Spinner 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/spinner"
        android:layout_below="@id/file_seletced"/>
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/send"
        android:id="@+id/send"
        android:layout_below="@id/spinner"
        android:layout_alignRight="@id/spinner"/>
     <EditText 
        android:layout_height="wrap_content"
        android:layout_width="150dp"
        android:layout_below="@id/send"
        android:layout_alignParentLeft="true"
        android:hint="@string/ip"
        android:id="@+id/ip"/>
    <EditText 
        android:layout_height="wrap_content"
        android:layout_width="80dp"
        android:layout_toRightOf="@id/ip"
        android:hint="@string/port"
        android:layout_below="@id/send"
        android:layout_marginLeft="5dp"
        android:id="@+id/port"/>
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/port"
        android:layout_below="@id/send"
        android:id="@+id/sure"
        android:layout_alignParentRight="true"
        android:text="@string/sure"/>
    <!--  <ProgressBar 
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_below="@id/sure"
        android:visibility="gone"
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:progress="2"
        android:secondaryProgress="4"/> -->

    <Button 
        android:layout_width="90dp"
        android:layout_height="wrap_content"
        android:text="@string/exit"
        android:id="@+id/exit"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"/>
    <Button 
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:text="@string/disconnect"
        android:id="@+id/disconnect"
        android:layout_toLeftOf="@id/exit"
        android:layout_alignParentBottom="true"/>
    <Button 
        android:layout_height="wrap_content"
        android:layout_width="120dp"
        android:text="@string/connect"
        android:id="@+id/connect"
        android:layout_toLeftOf="@id/disconnect"
        android:layout_alignParentBottom="true"/>





</RelativeLayout>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!