Search code examples
c#windows-8.1multipartform-datakaltura

Upload files to service


I'm trying to adapt Kaltura API so it would work with Windows 8.1 application, and I am having problems with this function:

private void PostMultiPartWithFiles(HttpWebRequest request, KalturaParams kparams, KalturaFiles kfiles)
{
        string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
        request.ContentType = "multipart/form-data; boundary=" + boundary;

        // use a memory stream because we don't know the content length of the request when we have multiple files
        MemoryStream memStream = new MemoryStream();
        byte[] buffer;
        int bytesRead = 0;

        StringBuilder sb = new StringBuilder();
        sb.Append("--" + boundary + "\r\n");
        foreach (KeyValuePair<string, string> param in kparams)
        {
            sb.Append("Content-Disposition: form-data; name=\"" + param.Key + "\"" + "\r\n");
            sb.Append("\r\n");
            sb.Append(param.Value);
            sb.Append("\r\n--" + boundary + "\r\n");
        }

        buffer = Encoding.UTF8.GetBytes(sb.ToString());
        memStream.Write(buffer, 0, buffer.Length);

        foreach (KeyValuePair<string, FileStream> file in kfiles)
        {
            sb = new StringBuilder();
            FileStream fileStream = file.Value;
            sb.Append("Content-Disposition: form-data; name=\"" + file.Key + "\"; filename=\"" + Path.GetFileName(fileStream.Name) + "\"" + "\r\n");
            sb.Append("Content-Type: application/octet-stream" + "\r\n");
            sb.Append("\r\n");

            // write the current string builder content
            buffer = Encoding.UTF8.GetBytes(sb.ToString());
            memStream.Write(buffer, 0, buffer.Length);

            // write the file content
            buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
            bytesRead = 0;
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                memStream.Write(buffer, 0, bytesRead);

            buffer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
            memStream.Write(buffer, 0, buffer.Length);
        }

        request.ContentLength = memStream.Length;

        Stream requestStream = request.GetRequestStream();
        // write the memorty stream to the request stream
        memStream.Seek(0, SeekOrigin.Begin);
        buffer = new Byte[checked((uint)Math.Min(4096, (int)memStream.Length))];
        bytesRead = 0;
        while ((bytesRead = memStream.Read(buffer, 0, buffer.Length)) != 0)
            requestStream.Write(buffer, 0, bytesRead);

        requestStream.Close();
        memStream.Close();
 }

I have tried something like this:

private void PostMultiPartWithFiles(HttpWebRequest request, KalturaParams kparams, KalturaFiles kfiles)
{
    using (request)
    {
        string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
        MultipartFormDataContent content = new MultipartFormDataContent(boundary);
        foreach (KeyValuePair<string, string> param in kparams)
         {
             Stream fileStream = await new StringContent(param.Value).ReadAsStreamAsync();
             StreamContent streamContent = new StreamContent(fileStream);
             streamContent.Headers.Add("Content-Disposition", "form-data; name=\"" + param.Key + "\"");
             content.Add(streamContent);
         }
        foreach (var file in kfiles)
        {
            StorageFile storageFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(file.Value.Path);
            var randomAccessStream = await storageFile.OpenReadAsync();
            Stream stream = randomAccessStream.AsStreamForRead();
            StreamContent streamContent = new StreamContent(stream);
            content.Headers.Add("Content-Disposition", "form-data; name=\"" + file.Key + "\"; filename=\"" + file.Value.Name + "\"");
            content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            content.Add(streamContent, "fileData", file.Value.Path);
        }
    }
}

But it's not working. It says that fileData is not attached. I haven't really worked with those web things before, so maybe I'm doing it all wrong.


Solution

  • So I finally got how to do it. My code looks like this:

    byte[] concatinated = null;
    string boundary = string.Empty;
    boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
    byte[] buffer;
    
    StringBuilder sb = new StringBuilder();
    sb.Append("--" + boundary + "\r\n");
    foreach (KeyValuePair<string, string> param1 in kparams)
    {
        sb.Append("Content-Disposition: form-data; name=\"" + param1.Key + "\"" + "\r\n");
        sb.Append("\r\n");
        sb.Append(param1.Value);
        sb.Append("\r\n--" + boundary + "\r\n");
    }
    buffer = Encoding.UTF8.GetBytes(sb.ToString());
    
    byte[] fileBytes = null;
    byte[] filebytes2 = null;
    foreach (KeyValuePair<string, StorageFile> file in kfiles)
    {
        StorageFile storageFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(file.Value.Path);
        using (IRandomAccessStreamWithContentType stream = await storageFile.OpenReadAsync())
        {
            fileBytes = new byte[stream.Size];
    
            using (DataReader reader = new DataReader(stream))
            {
                await reader.LoadAsync((uint)stream.Size);
                reader.ReadBytes(fileBytes);
            }
        }
        filebytes2 = BuildByteArray("fileData", storageFile.Name, fileBytes, boundary);
    
        concatinated = new byte[buffer.Length + filebytes2.Length];
        System.Buffer.BlockCopy(buffer, 0, concatinated, 0, buffer.Length);
        System.Buffer.BlockCopy(filebytes2, 0, concatinated, buffer.Length, filebytes2.Length);
        buffer = new byte[concatinated.Length];
        concatinated.CopyTo(buffer, 0);
    }
    

    And

    private byte[] BuildByteArray(string name, string fileName, byte[] fileBytes, string boundary)
    {
            // Create multipart/form-data headers.
            byte[] firstBytes = Encoding.UTF8.GetBytes(String.Format(
                "--{0}\r\n" +
                "Content-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\n" +
                "\r\n",
                boundary,
                name,
                fileName));
    
            byte[] lastBytes = Encoding.UTF8.GetBytes(String.Format(
                "\r\n" +
                "--{0}--\r\n",
                boundary));
    
            int contentLength = firstBytes.Length + fileBytes.Length + lastBytes.Length;
            byte[] contentBytes = new byte[contentLength];
    
            // Join the 3 arrays into 1.
            Array.Copy(
                firstBytes,
                0,
                contentBytes,
                0,
                firstBytes.Length);
            Array.Copy(
                fileBytes,
                0,
                contentBytes,
                firstBytes.Length,
                fileBytes.Length);
            Array.Copy(
                lastBytes,
                0,
                contentBytes,
                firstBytes.Length + fileBytes.Length,
                lastBytes.Length);
    
            return contentBytes;
      }
    

    And then I use it like this:

    HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, url);
    requestMessage.Content = new ByteArrayContent(concat);
    requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data");
    requestMessage.Content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("boundary", boundary));
    var response = await client.SendAsync(requestMessage);
    var content = await response.Content.ReadAsStringAsync();
    

    The idea and second function I got from this article.