1

I am new to Web API and REST services and looking to build a simple REST server which accepts file uploads. I found out grapevine which is simple and easy to understand. I couldn't find any file upload example?

This is an example using System.Web.Http

 var streamProvider = new MultipartFormDataStreamProvider(ServerUploadFolder);

 await Request.Content.ReadAsMultipartAsync(streamProvider);

but the grapevine Request property does not have any method to do that. Can someone point me to an example?

g t
  • 7,287
  • 7
  • 50
  • 85
Ajax
  • 13
  • 3

1 Answers1

0

If you are trying to upload a file as a binary payload, see this question/answer on GitHub.

If you are trying to upload a file from a form submission, that will be a little bit trickier, as the multi-part payload parsers haven't been added yet, but it is still possible.

The following code sample is complete untested, and I just wrote this off the top of my head, so it might not be the best solution, but it's a starting point:

public static class RequestExtensions
{
    public static IDictionary<string, string> ParseFormUrlEncoded(this IHttpRequest request)
    {
        var data = new Dictionary<string, string>();

        foreach (var tuple in request.Payload.Split('='))
        {
            var parts = tuple.Split('&');
            var key = Uri.UnescapeDataString(parts[0]);
            var val = Uri.UnescapeDataString(parts[1]);
            if (!data.ContainsKey(key)) data.Add(key, val);
        }

        return data;
    }

    public static IDictionary<string, FormElement> ParseFormData(this IHttpRequest request)
    {
        var data = new Dictionary<string, FormElement>();
        var boundary = GetBoundary(request.Headers.Get("Content-Type"));

        if (boundary == null) return data;

        foreach (var part in request.Payload.Split(new[] { boundary }, StringSplitOptions.RemoveEmptyEntries))
        {
            var element = new FormElement(part);
            if (!data.ContainsKey(element.Name)) data.Add(element.Name, element);
        }

        return data;
    }

    private static string GetBoundary(string contenttype)
    {
        if (string.IsNullOrWhiteSpace(contenttype)) return null;

        return (from part in contenttype.Split(';', ',')
            select part.TrimStart().TrimEnd().Split('=')
            into parts
            where parts[0].Equals("boundary", StringComparison.CurrentCultureIgnoreCase)
            select parts[1]).FirstOrDefault();
    }
}

public class FormElement
{
    public string Name => _dispositionParams["name"];
    public string FileName => _dispositionParams["filename"];
    public Dictionary<string, string> Headers { get; private set; }
    public string Value { get; }

    private Dictionary<string, string> _dispositionParams;

    public FormElement(string data)
    {
        var parts = data.Split(new [] { "\r\n\r\n", "\n\n" }, StringSplitOptions.None);
        Value = parts[1];

        ParseHeaders(parts[0]);
        ParseParams(Headers["Content-Disposition"]);
    }

    private void ParseHeaders(string data)
    {
        Headers = data.TrimStart().TrimEnd().Split(new[] {"\r\n", "\n"}, StringSplitOptions.RemoveEmptyEntries).Select(header => header.Split(new[] {':'})).ToDictionary(parts => parts[0].TrimStart().TrimEnd(), parts => parts[1].TrimStart().TrimEnd());
    }

    private void ParseParams(string data)
    {
        _dispositionParams = new Dictionary<string, string>();

        foreach (var part in data.Split(new[] {';'}))
        {
            if (part.IndexOf("=") == -1) continue;
            var parts = part.Split(new[] {'='});
            _dispositionParams.Add(parts[0].TrimStart(' '), parts[1].TrimEnd('"').TrimStart('"'));
        }
    }
}

If you are looking for something async to use immediately, you can try to implement the answer to this stackoverflow question, which has not been tested by me.

Community
  • 1
  • 1
Scott Offen
  • 6,933
  • 3
  • 21
  • 24