Android上傳文件到PC的簡單實例

      最近一直在完成個任務,有關Android手機文件傳輸的,如今先作了一步,實現了手機能夠上傳文件到pc端。java

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

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;

這些都很簡單吧~app

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作準備的吧~socket

/** 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入門的書,這些都會有的~ide

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

 

//退出
        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>

固然整個程序的bug仍是很明顯的~不過基本功能以及能夠實現~不足之處亟待完善~但願你們多多指教~

第一次寫博客哦~哈哈!

相關文章
相關標籤/搜索