question

FeiXue-MSFT avatar image
2 Votes"
FeiXue-MSFT asked RoyLi-MSFT answered

Share a large file between two UWP apps using Stream Sockets

I need to share a large file between two UWP apps which are running in two different machines using stream sockets. In order to share the file successfully, I have to share file size before sharing the actual file. So I had to set a separate event in separate page to share the file size. Otherwise, the shared file is corrupted and can't open.

I have provided related code for server and client below.

Server - First Page

   private async void init_Click (object sender, RoutedEventArgs e) {
       byte[] byteArray;
       StorageFolder localFolder = ApplicationData.Current.LocalFolder;
       StorageFile zipFile = await localFolder.GetFileAsync (App.fileName);
       await Frame.Dispatcher.RunAsync (CoreDispatcherPriority.Normal, async () => {
           using (Stream stream = await zipFile.OpenStreamForReadAsync ()) {
               using (var memoryStream = new MemoryStream ()) {
                   stream.CopyTo (memoryStream);
                   byteArray = memoryStream.ToArray ();
               }
           }
           StreamWriter streamWriter = new StreamWriter (App.outputStream);
           await streamWriter.WriteLineAsync (byteArray.Length.ToString ());
           await streamWriter.FlushAsync ();
           await App.outputStream.FlushAsync ();
       });
   }

Second Page

 private async void ShareVideo_Click (object sender, RoutedEventArgs e) {
     byte[] byteArray;
     StorageFolder localFolder = ApplicationData.Current.LocalFolder;
     StorageFile zipFile = await localFolder.GetFileAsync (App.fileName);
     await Frame.Dispatcher.RunAsync (CoreDispatcherPriority.Normal, async () => {
         using (Stream stream = await zipFile.OpenStreamForReadAsync ()) {
             using (var memoryStream = new MemoryStream ()) {
                 stream.CopyTo (memoryStream);
                 byteArray = memoryStream.ToArray ();
             }
         }
         App.outputStream.Write (byteArray, 0, byteArray.Length);
         await App.outputStream.FlushAsync ();
     });
 }

Client

 private async void Page_Loaded (object sender, RoutedEventArgs e) {
     if (App.clientSocket != null) {
         StreamReader streamReader = new StreamReader (App.inputStream);
         string size = await streamReader.ReadLineAsync ();
         if (size != null) {
             App.fileSize = Convert.ToInt32 (size);
             byte[] buffer = new byte[App.fileSize];
             await App.inputStream.ReadAsync (buffer, 0, App.fileSize);
             string copiedFileName = "test.zip";
             StorageFolder localFolder = ApplicationData.Current.LocalFolder;
             StorageFile file = await localFolder.CreateFileAsync (copiedFileName, CreationCollisionOption.ReplaceExisting);
             await FileIO.WriteBytesAsync (file, buffer);
             await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync (CoreDispatcherPriority.Normal, async () => {
                 MessageDialog successMsg = new MessageDialog ("Shared zip file.");
                 successMsg.Title = "Success Message";
                 IAsyncOperation show = successMsg.ShowAsync ();
                 await Task.Delay (TimeSpan.FromSeconds (1));
                 show.Cancel ();
             });
         }
     }
 }


When I send file size and actual file in one event, it sets file size correctly on the client-side even though file sharing gets corrupted.

How can I send file size and actual data from one event to share the file successfully? Any idea on this is much appreciated.

Thanks



windows-uwp
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

RoyLi-MSFT avatar image
1 Vote"
RoyLi-MSFT answered

Hello,

Welcome to our Microsoft Q&A platform!

When you transfer a file, you can write the file size directly into the stream. It is recommended to use DataWriter as following:

 writer = new DataWriter(socket.OutputStream);
 // if we have a StorageFile named file
 using (var stream=await file.OpenStreamForReadAsync())
 using(var ms=new MemoryStream())
 {
     await stream.CopyToAsync(ms);
     var bytes = ms.ToArray();
     writer.WriteUInt32(Convert.ToUInt32(bytes.Length));
     writer.WriteBytes(ms.ToArray());
     await writer.StoreAsync();
 }

The size of the incoming stream can be pre-read by the DataReader when receiving the file as following:

 var reader = new DataReader(args.Socket.InputStream);
 try
 {
     while (true)
     {
         //Read first 4 bytes(length of the subsequent string).
         uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
         if (sizeFieldCount != sizeof(uint))
         {
             // The underlying socket was closed before we were able to read the whole data.
             return;
         }
 // Read the string.
         uint fileLength = reader.ReadUInt32();
         uint actualFileLength = await reader.LoadAsync(fileLength);
         if (fileLength != actualFileLength)
         {
             // The underlying socket was closed before we were able to read the whole data.
             return;
         }
 var file =await ApplicationData.Current.LocalFolder.CreateFileAsync("test.zip", CreationCollisionOption.ReplaceIfExists);
         var bytes =new byte[actualFileLength];
         reader.ReadBytes(bytes);
            
         await FileIO.WriteBytesAsync(file, bytes);
     }
 }
 catch (Exception exception)
 {
     // If this is an unknown status it means that the error is fatal and retry will likely fail.
        
 }

The above simple code that explains how to combine the file size with the actual content of the file. I hope it will help you.

Thanks


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.