22

I am using RestSharp (version 105.2.3.0 in Visual Studio 2013, .net 4.5) to call a NodeJS hosted webservice. One of the calls I need to make is to upload a file. Using a RESTSharp request, if I retrieve the stream from my end into a byte array and pass that to AddFile, it works fine. However, I would much rather stream the contents and not load up entire files in server memory (the files can be 100's of MB).

If I set up an Action to copy my stream (see below), I get an exception at the "MyStream.CopyTo" line of System.Net.ProtocolViolationException (Bytes to be written to the stream exceed the Content-Length bytes size specified). This exception is thrown within the Action block after client.Execute is called.

From what I read, I should not be manually adding a Content-Length header, and it doesn't help if I do. I have tried setting CopyTo buffer too small and large values, as well as omitting it entirely, to no avail. Can somebody give me a hint on what I've missed?

    // Snippet...
    protected T PostFile<T>(string Resource, string FieldName, string FileName,
        string ContentType, Stream MyStream, 
        IEnumerable<Parameter> Parameters = null) where T : new()
    {
        RestRequest request = new RestRequest(Resource);
        request.Method = Method.POST;

        if (Parameters != null)
        {
            // Note:  parameters are all UrlSegment values
            request.Parameters.AddRange(Parameters);
        }

        // _url, _username and _password are defined configuration variables
        RestClient client = new RestClient(_url);
        if (!string.IsNullOrEmpty(_username))
        {
            client.Authenticator = new HttpBasicAuthenticator(_username, _password);
        }

        /*
        // Does not work, throws System.Net.ProtocolViolationException,
        // Bytes to be written to the stream exceed the 
        // Content-Length bytes size specified.
        request.AddFile(FieldName, (s) =>
        {
            MyStream.CopyTo(s);
            MyStream.Flush();
        }, FileName, ContentType);
        */

        // This works, but has to load the whole file in memory
        byte[] data = new byte[MyStream.Length];
        MyStream.Read(data, 0, (int) MyStream.Length);
        request.AddFile(FieldName, data, FileName, ContentType);

        var response = client.Execute<T>(request);

        // check response and continue...
    }
Bjarki Heiðar
  • 3,117
  • 6
  • 27
  • 40
Jason
  • 941
  • 1
  • 10
  • 19
  • 1
    I just discovered the same problem. My line `request.AddFile("file", stream => data.CopyTo(stream), fileName)` got broken with RestSharp version 105.2.0+ . When downgrading to 105.1.0 it works fine. Haven't had time to investigate the changes in the git-repo yet... – anve Oct 19 '15 at 08:28
  • I have this problem too. Downgrading to 105.1.0 helped, but I also added a comment to old [issue on github](https://github.com/restsharp/RestSharp/issues/70) – Dmitry Oct 23 '15 at 12:57
  • You can use AddFileBytes is some sort of wrapper – hpfs Sep 16 '16 at 15:25

2 Answers2

18

I had the same issue. I ended up using the .Add() on the Files collection. It has a FileParameter param which has the same params as AddFile(), you just have to add the ContentLength:

var req = GetRestRequest("Upload", Method.POST, null);
//req.AddFile("file",
//    (s) => {
//        var stream = input(imageObject);
//        stream.CopyTo(s);
//        stream.Dispose();
//    },
//    fileName, contentType);

req.Files.Add(new FileParameter {
    Name = "file",
    Writer = (s) => {
        var stream = input(imageObject);
        stream.CopyTo(s);
        stream.Dispose();
    },
    FileName = fileName,
    ContentType = contentType,
    ContentLength = contentLength
});            
Ben
  • 1,169
  • 3
  • 17
  • 27
  • 6
    what is the `input` ? I got error on this. how to declare it ? – Root Jul 11 '19 at 09:01
  • 1
    @Root If you have your file as a `byte[]`, you can convert it into a `MemoryStream` and refer to it. Worked like a charm for me: `Stream stream = new MemoryStream(fileBytes);` Then below just remove the line in question and refer to your new `Stream` object. – corbin Apr 06 '21 at 21:17
  • 1
    This is outdated. req.Files.Add no longer exists in v4.0 – Robert Barrueco Feb 24 '22 at 20:05
-1

The following code works for me for uploading a csv file using rest sharp. Web services API has been called.

var client = new RestClient(<YOUR API END URL >);
var request = new RestRequest(Method.POST) ;
request.AlwaysMultipartFormData = true;
request. AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("X-API-TOKEN", <Your Unique Token - again not needed for certain calls>);
request.AddParameter(<Your parameters.....>);
request.AddFile("file", currentFileLocation, contentType);
request.AddParameter("multipart/form-data", fileName, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
var response = client.Execute(request);
MasterAler
  • 1,614
  • 3
  • 23
  • 35
Paul George
  • 45
  • 2
  • 9