APK文件 (对应的windows服务器端已经架设好,可以直接下载进行测试)
源码 数据库文件
在前面一篇文章:【源码】c#编写的安卓客户端与Windows服务器程序进行网络通信 中我们探讨了,如何通过xamarin技术,完成安卓客户端与Windows服务器的通信,这篇文章,我们探讨一下使用场景非常多的文件传输.
先谈一下为什么使用xamarin.android技术吧,之前有开发过一个公文系统,c#语言开发,服务器部署在Windows Server 2003上,客户端采用Winform技术(.net2.0),使用了一段时间后,客户提出希望系统能够支持安卓移动端。首先想到了用java语言进行开发,用java写安卓程序应该是最好不过了,但是难点出现了,就是如何让java编写的安卓客户端与现有的Windows服务器上的程序通信,探索多日无果,于是想起了xamarin.adnroid技术,使用此技术,可以集成原有的C#通信框架,TCP通信这一块就解决了.这样做还有一个好处,即能够与原有的服务器端程序无缝集成,服务器端程序同时支持Windows客户端与安卓客户端。
学习Xamarin.Android的时间不长,水平有限,希望本文能够抛砖引玉,对xamarin开发有经验的朋友请多多指点,不足之处敬请批评指正。
本Demo效果图如下
当用户点击“从服务器获取文件”按钮后,服务器端会收到相应的请求,并开始通过TCP连接发送数据,本例中,服务器发送一张图片(大小为20k),客户端收到后,新建一个名称为"msdc"的文件夹,并把文件存储在此文件夹中。`
我们来看一下开发过程:
第一步:在Main.axml文件中,增加一个按钮
<Button
android:id="@+id/btnGetFile"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="从服务器获取文件" />
第二步:
客户端的MainActivity.cs文件中,编写该按钮相对应的方法
Button buttonGetFile = FindViewById<Button>(Resource.Id.btnGetFile);
buttonGetFile.Click += new EventHandler(buttonGetFile_Click);
void buttonGetFile_Click(object sender, EventArgs e)
{
GetFileFromServer();
}
public void GetFileFromServer()
{
//传递的参数为本地保存的路径
string filePath = GetFileSavePath(this);
//发送一个请求给服务器,服务器收到该请求后,开始发送文件
newTcpConnection.SendObject ("GetFileFromServer", filePath);
}
private String GetFileSavePath(Context context)
{
String filePath;
if (checkSDCard())
{
filePath = Android.OS.Environment.GetExternalStoragePublicDirectory("").ToString() + @"/MSDC/";//File.Separator
}
else
{
filePath = context.CacheDir.AbsolutePath + @"/MSDC/";
}
Java.IO.File file = new Java.IO.File(filePath);
if (!file.Exists())
{
Boolean b = file.Mkdirs();
}
else
{
}
return filePath;
}
GetFileSavePath
//检测是否存在SD卡
private Boolean checkSDCard()
{
if (Android.OS.Environment.ExternalStorageState.Equals(Android.OS.Environment.MediaMounted))
{
return true;
}
else
{
return false;
}
}
checkSDCard 检查是否存在SD卡
第三步:看一下服务器端的处理程序
private void IncomingReqMobileUpFile(PacketHeader header, Connection connection, string filePath)
{
//在此Demo中,我们直接指定一个文件,进行发送
string filename = AppDomain.CurrentDomain.BaseDirectory + "Files\\" + "msdc.jpg";
string fileID = FileIDCreator.GetNextFileID(NetworkComms.NetworkIdentifier.ToString());
SendFile sendFile = new SendFile(fileID, filename, filePath, connection, customOptions );
sendFile.NowSendFile();
}
using System;
using System.Collections.Generic;
using System.Text;
using NetworkCommsDotNet;
using System.ComponentModel;
using System.IO;
using NetworkCommsDotNet;
using DPSBase;
using Mobile.Entity ;
using System.Threading ;
namespace MobileServer
{
public class SendFile
{
//取消文件的发送
private volatile bool canceled = false;
private FileTransFailReason fleTransFailReason = FileTransFailReason.Error ;
/// <summary>
/// The name of the file
/// 文件名
/// </summary>
public string Filename { get; private set; }
/// <summary>
/// The connectionInfo corresponding with the source
/// 连接信息
/// </summary>
/// <summary>
/// 收发参数
/// </summary>
private SendReceiveOptions sendReceiveOptions;
public SendReceiveOptions SendReceiveOptions
{
get { return sendReceiveOptions; }
set { sendReceiveOptions = value; }
}
private Connection connection;
public Connection Connection
{
get { return connection; }
set { connection = value; }
}
//文件ID 用于管理文件 和文件的发送 取消发送相关
private string fileID;
public string FileID
{
get { return fileID; }
set { fileID = value; }
}
//文件传输后存储的路径 客户端传过来的路径 再传回去
private string filePath;
public string Filepath
{
get { return filePath; }
set { filePath = value; }
}
/// <summary>
/// The total size in bytes of the file
/// 文件的字节大小
/// </summary>
public long SizeBytes { get; private set; }
/// <summary>
/// The total number of bytes received so far
/// 目前收到的文件的带下
/// </summary>
public long SentBytes { get; private set; }
/// <summary>
/// Getter which returns the completion of this file, between 0 and 1
///已经完成的百分比
/// </summary>
public double CompletedPercent
{
get { return (double)SentBytes / SizeBytes; }
//This set is required for the application to work
set { throw new Exception("An attempt to modify read-only value."); }
}
/// <summary>
/// A formatted string of the SourceInfo
/// 源信息
/// </summary>
/// <summary>
/// Returns true if the completed percent equals 1
/// 是否完成
/// </summary>
public bool IsCompleted
{
get { return SentBytes == SizeBytes; }
}
/// <summary>
/// Private object used to ensure thread safety
/// </summary>
object SyncRoot = new object();
/// <summary>
///Event subscribed to by GUI for updates
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Create a new ReceivedFile
/// </summary>
/// <param name="filename">Filename associated with this file</param>
/// <param name="sourceInfo">ConnectionInfo corresponding with the file source</param>
/// <param name="sizeBytes">The total size in bytes of this file</param>
public SendFile(string fileID, string filename, string filePath, Connection connection, SendReceiveOptions sendReceiveOptions )
{
//文件ID
this.fileID = fileID;
this.Filename = filename;
this.filePath = filePath;
this.connection = connection;
this.sendReceiveOptions = sendReceiveOptions;
}
public void NowSendFile()
{
new Action(this.StartSendFile).BeginInvoke(null, null);
}
public void StartSendFile()
{
try
{
//Create a fileStream from the selected file
//根据选择的文件创建一个文件流
FileStream stream = new FileStream(this.Filename, FileMode.Open, FileAccess.Read);
//Wrap the fileStream in a threadSafeStream so that future operations are thread safe
//包装成线程安全的数据流
ThreadSafeStream safeStream = new ThreadSafeStream(stream);
//Get the filename without the associated path information
//获取不包含路径信息的文件名
string shortFileName = System.IO.Path.GetFileName(Filename);
long sendChunkSizeBytes = 4096;
this.SizeBytes = stream.Length;
long totalBytesSent = 0;
do
{
//Check the number of bytes to send as the last one may be smaller
long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent);
//Wrap the threadSafeStream in a StreamSendWrapper so that we can get NetworkComms.Net
//to only send part of the stream.
StreamSendWrapper streamWrapper = new StreamSendWrapper(safeStream, totalBytesSent, bytesToSend);
//We want to record the packetSequenceNumber
//我们希望记录包的顺序号
long packetSequenceNumber;
//Send the select data
connection.SendObject("PartialFileData", streamWrapper, sendReceiveOptions, out packetSequenceNumber);
//Send the associated SendInfo for this send so that the remote can correctly rebuild the data
//把包的顺序号记录在 SendInfo类中。
connection.SendObject("PartialFileDataInfo", new SendInfo(fileID, shortFileName, filePath, stream.Length, totalBytesSent, packetSequenceNumber), sendReceiveOptions);
totalBytesSent += bytesToSend;
//更新已经发送的字节的属性
SentBytes += bytesToSend;
////Update the GUI with our send progress
//UpdateSendProgress((double)totalBytesSent * 100 / stream.Length);
if (! this.canceled)
{
Thread.Sleep(30);
}
} while ((totalBytesSent < stream.Length) && !this.canceled);
//AddLineToLog("Completed file send to '" + connection.ConnectionInfo.ToString() + "'.");
}
catch (CommunicationException)
{
}
catch (Exception ex)
{
}
}
}
}
SendFile方法
------解决思路----------------------
有分的话就支持一下
------解决思路----------------------
有分的话就支持一下
------解决思路----------------------
------解决思路----------------------
支持