110,533
社区成员
发帖
与我相关
我的任务
分享
<Button
android:id="@+id/btnGetFile"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="从服务器获取文件" />
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方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System.IO;
using NetworkCommsDotNet;
using DPSBase;
using Mobile.Entity;
namespace Mobile.Client
{
public class ReceivedFile
{
//////传输过程
////public event Action<string, long, long> FileTransProgress;
//////传输完成
public event Action<string> FileTransCompleted;
//////传输中断
////public event Action<string, FileTransDisrupttedType> FileTransDisruptted;
/// <summary>
/// The name of the file
/// 文件名 (没有带路径)
/// </summary>
public string Filename { get; private set; }
/// <summary>
/// The connectionInfo corresponding with the source
/// 连接信息
/// </summary>
public ConnectionInfo SourceInfo { get; private set; }
//文件ID 用于管理文件 和文件的发送 取消发送相关
private string fileID;
public string FileID
{
get { return fileID; }
set { fileID = 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 ReceivedBytes { get; private set; }
/// <summary>
/// Getter which returns the completion of this file, between 0 and 1
///已经完成的百分比
/// </summary>
public double CompletedPercent
{
get { return (double)ReceivedBytes / 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>
public string SourceInfoStr
{
get { return "[" + SourceInfo.RemoteEndPoint.ToString() + "]"; }
}
/// <summary>
/// Returns true if the completed percent equals 1
/// 是否完成
/// </summary>
public bool IsCompleted
{
get { return ReceivedBytes == SizeBytes; }
}
/// <summary>
/// Private object used to ensure thread safety
/// </summary>
object SyncRoot = new object();
/// <summary>
/// A memory stream used to build the file
/// 用来创建文件的数据流
/// </summary>
Stream data;
/// <summary>
///Event subscribed to by GUI for updates
/// </summary>
//临时文件流存储的位置
public string TempFilePath = "";
//文件最后的保存路径
public string SaveFilePath = "";
/// <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 ReceivedFile(string fileID, string filename, string filePath, ConnectionInfo sourceInfo, long sizeBytes)
{
string tempSizeBytes = sizeBytes.ToString();
this.fileID = fileID;
this.Filename = filename;
this.SourceInfo = sourceInfo;
this.SizeBytes = sizeBytes;
//如果临时文件已经存在,则添加.data后缀
this.TempFilePath = filePath + filename + ".data";
while (File.Exists(this.TempFilePath))
{
this.TempFilePath = this.TempFilePath + ".data";
}
this.SaveFilePath = filePath + filename;
//We create a file on disk so that we can receive large files
//我们在硬盘上创建一个文件,使得我们可以接收大的文件
data = new FileStream(TempFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 8 * 1024, FileOptions.DeleteOnClose);
}
/// <summary>
/// Add data to file
/// 添加数据到文件中
/// </summary>
/// <param name="dataStart">Where to start writing this data to the internal memoryStream</param>
/// <param name="bufferStart">Where to start copying data from buffer</param>
/// <param name="bufferLength">The number of bytes to copy from buffer</param>
/// <param name="buffer">Buffer containing data to add</param>
public void AddData(long dataStart, int bufferStart, int bufferLength, byte[] buffer)
{
lock (SyncRoot)
{
if (!this.canceled && (this.data != null))
{
try
{
data.Seek(dataStart, SeekOrigin.Begin);
data.Write(buffer, (int)bufferStart, (int)bufferLength);
ReceivedBytes += (int)(bufferLength - bufferStart);
////EventsHelper.Fire<string, long, long>(this.FileTransProgress, FileID, SizeBytes, ReceivedBytes);
if (ReceivedBytes == SizeBytes)
{
data.Flush();
SaveFileToDisk(SaveFilePath);
data.Close();
EventsHelper.Fire<string>(this.FileTransCompleted, FileID);
}
}
catch (Exception exception)
{
//触发文件传输中断事件
//this.FileTransDisruptted(Filename, FileTransDisrupttedType.InnerError);
////EventsHelper.Fire<string, FileTransDisrupttedType>(this.FileTransDisruptted, FileID, FileTransDisrupttedType.InnerError);
}
}
}
}
private volatile bool canceled;
public void Cancel(FileTransFailReason disrupttedType, bool deleteTempFile)
{
try
{
this.canceled = true;
this.data.Flush();
this.data.Close();
this.data = null;
if (deleteTempFile)
{
File.Delete(this.TempFilePath);
}
}
catch (Exception)
{
}
//通知 Receiver取消,并且触发文件传输中断事件
////EventsHelper.Fire<string, FileTransDisrupttedType>(this.FileTransDisruptted, FileID, FileTransDisrupttedType.InnerError);
}
/// <summary>
/// Saves the completed file to the provided saveLocation
/// 保存文件到指定位置
/// </summary>
/// <param name="saveLocation">Location to save file</param>
public void SaveFileToDisk(string saveLocation)
{
if (ReceivedBytes != SizeBytes)
throw new Exception("Attempted to save out file before data is complete.");
if (!File.Exists(TempFilePath))
throw new Exception("The transferred file should have been created within the local application directory. Where has it gone?");
//File.Delete(saveLocation);
//覆盖文件
File.Copy(TempFilePath, saveLocation, true);
}
/// <summary>
/// Closes and releases any resources maintained by this file
/// </summary>
public void Close()
{
try
{
data.Dispose();
}
catch (Exception) { }
try
{
data.Close();
}
catch (Exception) { }
}
}
}
ReceivedFile
//处理文件数据 <2>
NetworkComms.AppendGlobalIncomingPacketHandler<byte[]>("PartialFileData", IncomingPartialFileData);
//处理文件信息 <3>
NetworkComms.AppendGlobalIncomingPacketHandler<SendInfo>("PartialFileDataInfo", IncomingPartialFileDataInfo);
private void IncomingPartialFileData(PacketHeader header, Connection connection, byte[] data)
{
try
{
SendInfo info = null;
ReceivedFile file = null;
//Perform this in a thread safe way
lock (syncLocker)
{
//Extract the packet sequence number from the header
//The header can also user defined parameters
//获取数据包的顺序号
long sequenceNumber = header.GetOption(PacketHeaderLongItems.PacketSequenceNumber);
//如果数据信息字典包含 "连接信息" 和 "包顺序号"
if (incomingDataInfoCache.ContainsKey(connection.ConnectionInfo) && incomingDataInfoCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
{
//We have the associated SendInfo so we can add this data directly to the file
//根据顺序号,获取相关SendInfo记录
info = incomingDataInfoCache[connection.ConnectionInfo][sequenceNumber];
//从信息记录字典中删除相关记录
incomingDataInfoCache[connection.ConnectionInfo].Remove(sequenceNumber);
//Check to see if we have already initialised this file
//检查相关连接上的文件是否存在,如果不存在,则添加相关文件{ReceivedFile}
if (!receivedFiles.ContainsKey(info.FileID))
{
ReceivedFile receivedFile = new ReceivedFile(info.FileID, info.Filename, info.FilePath, connection.ConnectionInfo, info.TotalBytes);
receivedFile.FileTransCompleted += new Action<string>(this.receivedFile_FileTransCompleted);
receivedFiles.Add(info.FileID, receivedFile);
}
file = receivedFiles[info.FileID];
}
else
{
//We do not yet have the associated SendInfo so we just add the data to the cache
//如果不包含顺序号,也不包含相关"连接信息",添加相关连接信息
if (!incomingDataCache.ContainsKey(connection.ConnectionInfo))
incomingDataCache.Add(connection.ConnectionInfo, new Dictionary<long, byte[]>());
//在数据字典中添加相关"顺序号"的信息
incomingDataCache[connection.ConnectionInfo].Add(sequenceNumber, data);
}
}
//If we have everything we need we can add data to the ReceivedFile
if (info != null && file != null && !file.IsCompleted)
{
file.AddData(info.BytesStart, 0, data.Length, data);
//Perform a little clean-up
file = null;
data = null;
}
else if (info == null ^ file == null)
throw new Exception("Either both are null or both are set. Info is " + (info == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
}
catch (Exception ex)
{
//If an exception occurs we write to the log window and also create an error file
}
}
IncomingPartialFileData
private void IncomingPartialFileDataInfo(PacketHeader header, Connection connection, SendInfo info)
{
try
{
byte[] data = null;
ReceivedFile file = null;
//Perform this in a thread safe way
lock (syncLocker)
{
//Extract the packet sequence number from the header
//The header can also user defined parameters
//从 SendInfo类中获取相应数据类的信息号 以便可以对应。
long sequenceNumber = info.PacketSequenceNumber;
if (incomingDataCache.ContainsKey(connection.ConnectionInfo) && incomingDataCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
{
//We already have the associated data in the cache
data = incomingDataCache[connection.ConnectionInfo][sequenceNumber];
incomingDataCache[connection.ConnectionInfo].Remove(sequenceNumber);
//Check to see if we have already initialised this file
if (!receivedFiles.ContainsKey(info.FileID))
{
ReceivedFile receivedFile = new ReceivedFile(info.FileID, info.Filename, info.FilePath, connection.ConnectionInfo, info.TotalBytes);
receivedFile.FileTransCompleted += new Action<string>(this.receivedFile_FileTransCompleted);
receivedFiles.Add(info.FileID, receivedFile);
}
file = receivedFiles[info.FileID];
}
else
{
//We do not yet have the necessary data corresponding with this SendInfo so we add the
//info to the cache
if (!incomingDataInfoCache.ContainsKey(connection.ConnectionInfo))
incomingDataInfoCache.Add(connection.ConnectionInfo, new Dictionary<long, SendInfo>());
incomingDataInfoCache[connection.ConnectionInfo].Add(sequenceNumber, info);
}
}
//If we have everything we need we can add data to the ReceivedFile
if (data != null && file != null && !file.IsCompleted)
{
file.AddData(info.BytesStart, 0, data.Length, data);
//Perform a little clean-up
file = null;
data = null;
}
else if (data == null ^ file == null)
throw new Exception("Either both are null or both are set. Data is " + (data == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
}
catch (Exception ex)
{
}
}
IncomingPartialFileDataInfo
MainActivity.cs中添加相应的字典变量
//接收文件字典
Dictionary<string, ReceivedFile> receivedFiles = new Dictionary<string, ReceivedFile>();
/// <summary>
/// Incoming partial data cache. Keys are ConnectionInfo, PacketSequenceNumber. Value is partial packet data.
/// </summary>
Dictionary<ConnectionInfo, Dictionary<long, byte[]>> incomingDataCache = new Dictionary<ConnectionInfo, Dictionary<long, byte[]>>();
/// <summary>
/// Incoming sendInfo cache. Keys are ConnectionInfo, PacketSequenceNumber. Value is sendInfo.
/// </summary>
Dictionary<ConnectionInfo, Dictionary<long, SendInfo>> incomingDataInfoCache = new Dictionary<ConnectionInfo, Dictionary<long, SendInfo>>();