web-dev-qa-db-fra.com

Envoyer et recevoir des fichiers volumineux sur des flux dans ASP.NET Web Api C #

Je travaille sur un projet où j'ai besoin d'envoyer de gros fichiers audio via des flux d'un client vers un serveur. J'utilise ASP.NET Web Api pour communiquer entre le client et le serveur. Mon client a une méthode "SendFile" qui, je crois, fonctionne bien, mais je ne sais pas comment faire pour que mon serveur reçoive les données que j'envoie via un flux. Mon code client ressemble à ceci jusqu'à présent:

 private const int MAX_CHUNK_SIZE = (1024 * 5000);
    private HttpWebRequest webRequest = null;
    private FileStream fileReader = null;
    private Stream requestStream = null;

    public bool SendAudio(string uri, string file)
    {
        byte[] fileData;
        fileReader = new FileStream(file, FileMode.Open, FileAccess.Read);
        webRequest = (HttpWebRequest)WebRequest.Create(uri);
        webRequest.Method = "POST";
        webRequest.ContentLength = fileReader.Length;
        webRequest.Timeout = 600000;
        webRequest.Credentials = CredentialCache.DefaultCredentials;
        webRequest.AllowWriteStreamBuffering = false;
        requestStream = webRequest.GetRequestStream();

        long fileSize = fileReader.Length;
        long remainingBytes = fileSize;
        int numberOfBytesRead = 0, done = 0;

        while (numberOfBytesRead < fileSize)
        {
            SetByteArray(out fileData, remainingBytes);
            done = WriteFileToStream(fileData, requestStream);
            numberOfBytesRead += done;
            remainingBytes -= done;
        }
        fileReader.Close();
        return true;
    }

    public int WriteFileToStream(byte[] fileData, Stream requestStream)
    {
        int done = fileReader.Read(fileData, 0, fileData.Length);
        requestStream.Write(fileData, 0, fileData.Length);

        return done;
    }

    private void SetByteArray(out byte[] fileData, long bytesLeft)
    {
        fileData = bytesLeft < MAX_CHUNK_SIZE ? new byte[bytesLeft] : new byte[MAX_CHUNK_SIZE];
    }

Mon serveur ressemble à ceci:

    [HttpPost]
    [ActionName("AddAudio")]
    public async Task<IHttpActionResult> AddAudio([FromUri]string name)
    {
        try
        {
            isReceivingFile = true;
            byte[] receivedBytes = await Request.Content.ReadAsByteArrayAsync();
            if (WriteAudio(receivedBytes, name) == true)
            {
                isReceivingFile = false;
                return Ok();
            }
            else
            {
                isReceivingFile = false;
                return BadRequest("ERROR: Audio could not be saved on server.");
            }
        }
        catch (Exception ex)
        {
            isReceivingFile = false;
            return BadRequest("ERROR: Audio could not be saved on server.");
        }
    }

    public bool WriteAudio(byte[] receivedBytes, string fileName)
    {
        string file = Path.Combine(@"C:\Users\username\Desktop\UploadedFiles", fileName);
        using (FileStream fs = File.Create(file))
        {
            fs.Write(receivedBytes, 0, receivedBytes.Length);
        }
        return true;
    }

Le code du serveur a le code d'origine que j'ai écrit pour lui, avant de décider d'essayer de le faire fonctionner avec des flux. Le code du serveur fonctionne toujours si j'envoie un petit fichier (moins de 30 Mo), mais si j'envoie un fichier volumineux, mon serveur obtient une "exception de mémoire". Je ne peux pas comprendre comment faire en sorte que le serveur prenne les données via un flux. Dans ma recherche de solutions, je suis tombé sur de nombreux exemples avec des sockets et TCPClient, mais ce n'est pas comme cela que nous voulons le faire sur ce projet. Quelqu'un peut-il m'aider, s'il vous plaît?

14
Tobiuo

si j'envoie un fichier volumineux, mon serveur obtient une "exception de mémoire"

Eh bien, il lit le flux entier dans la mémoire ici:

byte[] receivedBytes = await Request.Content.ReadAsByteArrayAsync();

Ce que vous voulez faire, c'est copier le flux d'un endroit à un autre, sans tout charger en mémoire à la fois. Quelque chose comme ça devrait fonctionner:

[HttpPost]
[ActionName("AddAudio")]
public async Task<IHttpActionResult> AddAudio([FromUri]string name)
{
  try
  {
    string file = Path.Combine(@"C:\Users\username\Desktop\UploadedFiles", fileName);
    using (FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write,
        FileShare.None, 4096, useAsync: true))
    {
      await Request.Context.CopyToAsync(fs);
    }
    return Ok();
  }
  catch (Exception ex)
  {
    return BadRequest("ERROR: Audio could not be saved on server.");
  }
}
17
Stephen Cleary