Android上传文件到服务器中的简单实例

jopen 12年前

      最近一直在完成个任务,有关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>