dudeNumber4
dudeNumber4

Reputation: 4687

Can't Post a file to ASP.NET Web API from HttpClient

Using VS 2012, ASP.NET Web API 4.0, Web API Client Libraries for 4.0.
I've created a "hello world" controller, and got simple get/post working. I can't post a file, however.

I'm accessing the service via an HttpClient client, but I couldn't get a simple HTML page to work (posting a file) no matter what I tried either. However, that had it's own set of issues (and the content always came across IsMimeMultipartContent whereas my HttpClient doesn't); I'd just like to get an HttpClient working for now. The client streams in the file fine (I can see that the length is good), but on the server the length of the stream inside ReadAsStreamAsync is 0. Also, HttpContext.Current.Request.Files count is 0 (that's the case even when hitting from an HTML file post).

Here's the controller:

public class ImportController : ApiController
{

    public HttpResponseMessage Post()
    {
        WriteFile( HttpContext.Current.Server.MapPath( "~/killme.xml" ) );
        return new HttpResponseMessage( HttpStatusCode.Accepted );
    }

    private void WriteFile( string filePath )
    {
        Request.Content.ReadAsStreamAsync().ContinueWith( task =>
        {
            try
            {
                var requestStream = task.Result;
                var fileStream = File.Create( filePath );
                requestStream.CopyTo( fileStream );
                fileStream.Close();
                requestStream.Close();
            }
            catch
            {
                ;  // TBD
            }
        } );
    }

}

Here is the client:

var client = new HttpClient();
client.PostAsync( "http://localhost:52800/api/Import", ReadFile() ).ContinueWith( ( task ) =>
{
    Console.WriteLine( task.Result.StatusCode.ToString() );
    client.Dispose();
} );

private static StreamContent ReadFile()
{
    var fileBytes = File.ReadAllBytes( @"C:\temp\killme.xml" );
    var ms = new MemoryStream( fileBytes.Length );
    ms.Write( fileBytes, 0, fileBytes.Length );
    // Can't put this in using; don't know how to dispose
    return new StreamContent( ms );
}

Upvotes: 1

Views: 1716

Answers (1)

Darrel Miller
Darrel Miller

Reputation: 142014

I think your only error is forgetting to reset the position of the memory stream before passing it to StreamContent. However, I prefer to create a new derived HttpContent class specifically for dealing with sending files.

With this class your code would look like,

var client = new HttpClient();
var content = new FileContent(@"C:\temp\killme.xml");
client.PostAsync( "http://localhost:52800/api/Import", content ).ContinueWith( ( task ) =>
{
    Console.WriteLine( task.Result.StatusCode.ToString() );
    client.Dispose();
} );

E.g.

public class FileContent : HttpContent
    {
        private const int DefaultBufferSize = 1024 * 64;
        private readonly string _fileName;
        private readonly FileInfo _fileInfo;

       public FileContent(string fileName)
        {
            if (string.IsNullOrWhiteSpace(fileName))
            {
                throw new ArgumentNullException("fileName");
            }
            _fileInfo = new FileInfo(fileName);
            if (!_fileInfo.Exists)
            {
                throw new FileNotFoundException(string.Empty, fileName);
            }

            _fileName = fileName;

        }

        protected override bool TryComputeLength(out long length)
        {
            length = _fileInfo.Length;
            return true;
        }

        private FileStream _FileStream;
        protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            _FileStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite,
                                         DefaultBufferSize,
                                         FileOptions.Asynchronous | FileOptions.SequentialScan);

            _FileStream.CopyTo(stream);
            var tcs = new TaskCompletionSource<object>();
            tcs.SetResult(null);
            return tcs.Task;

        }

        protected override void Dispose(bool disposing)
        {
            if (_FileStream != null)
            {
                _FileStream.Dispose();
                _FileStream = null;
            }
            base.Dispose(disposing);
        }
    }
}

Upvotes: 1

Related Questions