Pisces.IM.Client.WPF(持续更新...)
Pisces.IM.Client.WPF
服务端监听各个客户端的连接情况

WPF 客户端

基础聊天

文件收发


历史记录
WPF 界面部分用到了MaterialDesignThemes(4.9.0)
emoji的支持:Emoji.Wpf(0.3.4)
Helper
ImageHelper.cs 处理WPF中图像的使用
1 internal static class ImageHelper 2 { 3 public static BitmapImage ToBitmapImage(this Bitmap bitmap) 4 { 5 var bitmapImage = new BitmapImage(); 6 7 using var ms = new MemoryStream(); 8 bitmap.Save(ms, ImageFormat.Png); 9 bitmapImage.BeginInit(); 10 bitmapImage.StreamSource = ms; 11 bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 12 bitmapImage.EndInit(); 13 bitmapImage.Freeze(); 14 15 return bitmapImage; 16 } 17 18 public static BitmapImage? ToBitmapImage(this string path) 19 { 20 if (!File.Exists(path)) return null; 21 22 var bi = new BitmapImage(); 23 bi.BeginInit(); 24 bi.UriSource = new Uri(path, UriKind.RelativeOrAbsolute); 25 bi.EndInit(); 26 return bi; 27 } 28 29 public static Bitmap ToBitmap(this BitmapSource source) 30 { 31 var bmp = new Bitmap(source.PixelWidth, source.PixelHeight, PixelFormat.Format32bppArgb); 32 var bmpData = bmp.LockBits(new System.Drawing.Rectangle(Point.Empty, bmp.Size), 33 ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); 34 source.CopyPixels(Int32Rect.Empty, bmpData.Scan0, bmpData.Height * bmpData.Stride, bmpData.Stride); 35 bmp.UnlockBits(bmpData); 36 37 var temp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format32bppArgb); 38 var g = Graphics.FromImage(temp); 39 g.DrawImage(bmp, new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height)); 40 g.Dispose(); 41 return temp; 42 } 43 44 }
获取系统中默认文件的的icon
1 private static ImageSource? GetFileIcon(string? fileName) 2 { 3 4 if (!File.Exists(fileName)) return null; 5 6 if (string.IsNullOrEmpty(fileName)) return null; 7 8 var icon = System.Drawing.Icon.ExtractAssociatedIcon(fileName); 9 10 return icon?.ToBitmap().ToBitmapImage(); 11 12 }
如下图:

Views
ChatView.xaml.cs
文件的收发
因为目前并未设计服务端进行消息和文件的缓存,所以文件的收发未采用FTP,目前选择Socket直接收发,目前文件限制到50M以内
同时将文件拆分各个小的数据包, 每个小数据包大小为2048byte,并包装成消息收发协议。
文件数据包的发送同样遵照Pisces.IM.Communication中对数据包处理。
发送文件之前,将告诉文件接收方,当文件接收方做好接收准备后并告知发送方可以发送文件数据了。
1 var fileTransferInfo = new FileTransferInfo() 2 { 3 FileId = GenerateIdHelper.GenerateId().ToString(), 4 FileExtension = FileHelper.GetFileExtension(file), 5 FileHashCode = FileHelper.GetFileSHA256HexString(file), 6 FileName = FileHelper.GetFileShortName(file), 7 FileSize = FileHelper.GetFileSize(file), 8 FromId = _globalDataContext.MySelfID, 9 ToId = SelectedItem.Id, 10 ReceiverStatus = false, 11 SenderStatus = true, 12 };
FileExtension :告知文件的类型
FileHashCode:告知文件的SHA256校验值,防止在传输过程中文件是否被修改或者损坏
FileSize:告知文件的大小
文件的拆分与发送
1 private void SendFile(FileTransferInfo fileTransferInfo) 2 { 3 4 Task.Run(() => 5 { 6 if (_globalDataContext == null) return; 7 if (_globalDataContext.FileTransfer == null) return; 8 9 var fileTransfer = _globalDataContext.FileTransfer; 10 11 if (string.IsNullOrEmpty(fileTransferInfo.ToId)) return; 12 if (string.IsNullOrEmpty(fileTransferInfo.FromId)) return; 13 if (string.IsNullOrEmpty(fileTransferInfo.FileId)) return; 14 if (string.IsNullOrEmpty(fileTransferInfo.FileName)) return; 15 var content = _chatContentViewModel.ChatContents[fileTransferInfo.ToId]; 16 17 if (content == null) return; 18 var fileContent = content.Where(x => x.FileContent?.FileId == fileTransferInfo.FileId).FirstOrDefault(); 19 if (fileContent == null) return; 20 if (fileContent.FileContent == null) return; 21 if (fileTransferInfo.FileSize <= 0) return; 22 23 var sdl = 1024 * 2; 24 //Number of slices 25 var nos = fileTransferInfo.FileSize / sdl; 26 //Final sliced data 27 long fsd = fileTransferInfo.FileSize % sdl; 28 29 using var steam = File.OpenRead(fileTransferInfo.FileName); 30 31 if (nos > 0) 32 { 33 var count = 0; 34 while (count < nos) 35 { 36 var sd = new byte[sdl]; 37 steam.Seek(sdl * count, SeekOrigin.Begin); 38 steam.Read(sd, 0, sdl); 39 var buffer = FilePacketService.Combined(fileTransferInfo.FromId, 40 fileTransferInfo.ToId, 1, sd, fileTransferInfo.FileId); 41 fileTransfer.GetCurrentSocket().Send(buffer); 42 count++; 43 fileContent.FileContent.CurrentFileSize += buffer.Length; 44 fileContent.FileContent.SetProgressValue(); 45 Thread.Sleep(5); 46 } 47 48 var sd_f = new byte[fsd]; 49 steam.Seek(sdl * count, SeekOrigin.Begin); 50 steam.Read(sd_f, 0, sd_f.Length); 51 var buffer_f = FilePacketService.Combined(fileTransferInfo.FromId, 52 fileTransferInfo.ToId, 1, sd_f, fileTransferInfo.FileId); 53 54 fileTransfer.GetCurrentSocket().Send(buffer_f); 55 56 fileContent.FileContent.CurrentFileSize += buffer_f.Length; 57 fileContent.FileContent.SetProgressValue(); 58 59 } 60 else 61 { 62 var sd = new byte[fileTransferInfo.FileSize]; 63 steam.Read(sd, 0, sd.Length); 64 var buffer = FilePacketService.Combined(fileTransferInfo.FromId, 65 fileTransferInfo.ToId, 1, sd, fileTransferInfo.FileId); 66 67 fileTransfer.GetCurrentSocket().Send(buffer); 68 69 fileContent.FileContent.CurrentFileSize += buffer.Length; 70 fileContent.FileContent.SetProgressValue(); 71 72 73 } 74 75 steam.Flush(); 76 steam.Close(); 77 fileContent.FileContent.ProcessbarVisibility = Visibility.Hidden; 78 }); 79 }
完整源码:github
浙公网安备 33010602011771号