Search code examples
xamarinasp.net-coreuploadhttpwebrequestazure-media-services

Upload large files to Azure Media Services in Xamarin


I am trying to upload a .mp4 file, selected from the user's iOS or Android device, to my Azure Media Services account.

This code works for small files ( less than ~95MB):

public static async Task<string> UploadBlob(string blobContainerSasUri, string blobName, byte[] blobContent, string path)
    {
        string responseString;
        int contentLength = blobContent.Length;
        string queryString = (new Uri(blobContainerSasUri)).Query;
        string blobContainerUri = blobContainerSasUri.Split('?')[0];
        string requestUri = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/{1}{2}", blobContainerUri, blobName, queryString);

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
        request.Method = "PUT";
        request.AllowWriteStreamBuffering = false;
        request.Headers.Add("x-ms-blob-type", "BlockBlob");
        request.ContentLength = contentLength;
        request.Timeout = Int32.MaxValue;
        request.KeepAlive = true;

        int bufferLength = 1048576; //upload 1MB at time, useful for a simple progress bar.

        Stream requestStream = request.GetRequestStream();
        requestStream.WriteTimeout = Int32.MaxValue;

        ProgressViewModel progressViewModel = App.Locator.GetProgressBar(App.Locator.MainViewModel.currentModuleItemId);
        MyVideosPage myVideosPage = App.Locator.GetVideosPage(App.Locator.MainViewModel.currentModuleItemId);

        FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
        int nRead = 0;
        int currentPos = 0;
        while ((nRead = fileStream.Read(blobContent, currentPos, bufferLength)) > 0)
        {

            await requestStream.WriteAsync(blobContent, currentPos, nRead);
            currentPos += nRead;

        }
        fileStream.Close();
        requestStream.Close();

        HttpWebResponse objHttpWebResponse = null;
        try
        {
            // this is where it fails for large files
            objHttpWebResponse = (HttpWebResponse)request.GetResponse();

            Stream responseStream = objHttpWebResponse.GetResponseStream();
            StreamReader stream = new StreamReader(responseStream);

            responseString = stream.ReadToEnd();                       
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            if (objHttpWebResponse != null)
                objHttpWebResponse.Close();
        }
        return responseString;
    }

An exception is thrown after this line is called:

(HttpWebResponse)request.GetResponse(); 

The exception message is "The request body is too large and exceeds the maximum permissible limit." The exception StatusCode is "RequestEntityTooLarge".

How can I upload large files? Is this a problem with HttpWebRequest, or Azure Media Services?


Solution

  • Azure Storage supports one shot upload (aka PutBlob API) up to 256MB if you are using the new REST API versions. But since you are not specifying the REST API version, you're defaulting to a very old version where the maximum supported size of one shot upload is 100MB.

    Use x-ms-version: 2018-03-28 header to be able to upload up to 256MB in one HTTP request.

    If you have to deal with larger files, you will need to use block & commit upload. You will need to use PutBlock API to stage blocks from the source file. Blocks can be up to 100MB each. Then you need to commit all the blocks using the PutBlockList API. If you don't have to deal with this logic yourself, simply use the Azure Storage SDK for .NET (supports Xamarin) and use the uploadFromFile method. It is simple, and resilient.