I'm selfhosting a service. I'm able to HttpGet and HttPut objects. Now I need to return a large File(stream). My question is how to return a large stream.
Below I write the methods I use to get and save a test class Customer.
Possible duplicates:
Selfhosting deal with large files. Alas the answer doesn't help me, it states: make sure that the response content is a StreamContent. Until now I didn't need to write any response content. What should I change to return a StreamContent?
ASP.NET Web API 2 - StreamContent is extremely slow This answer seems to describe the solution to my problem. A HttpRequestMessage object is used to create a HttpResponseMessage object. Then a StreamContent object is assigned to the HttpResponseMessage.Content. But where do I get a HttpRequestMessage, and what should I change in my signatures to be able to return a HttpResponseMessage?
So the duplicates do not help me enough. The answer leave me with a several question.
Until now I'm able to Get and Save an object using a [HttpGet] and [HttpPost]. In my simplified code below I get and save a Customer
To create my server according to the description given y MSDN: Use OWIN to Self-Host ASP.NET
Installed nuget: Microsoft.AspNet.WebApi.OwinSelfHost
Server Side
public class Customer {...}
[RoutePrefix("test")]
public class MyTestController : ApiController
{
[Rout("getcustomer")]
[HttpGet]
public Customer GetCustomer(int customerId)
{
Customer fetchedCustomer = ...;
return fetchedCustomer;
}
[Route("SaveCustomer")
[HttpPost]
public void SaveCustomer(Customer customer)
{
// code to save the customer
}
}
Server side: Main
static void Main(string[] args)
{
var owinserver = WebApp.Start("http://+:8080", (appBuilder) =>
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Formatters.Remove(config.Formatters.XmlFormatter);
appBuilder.UseWebApi(config);
config.EnsureInitialized();
});
Console.WriteLine("Press any key to end");
Console.ReadKey();
}
This is enough to get and set a customer. Apparently this is possible without a HttpRequestMessage.
So my questions:
What is the signature of a function to be able to return a big stream?
Is it enough to assign the StreamContent object as is proposed in the second duplicate?
Apparently the answer is easier than I thought.
In the examples I saw, the return value of a HttpGet was the object that you wanted to return:
[Route("getcustomer")]
[HttpGet]
public Customer GetCustomer(int customerId)
{
Customer fetchedCustomer = ...
return fetchedCustomer;
}
To return a stream, change the return value to a HttpResponseMessage and fill the Content of the HttpRespnseMessage with the Stream you want to return:
[Route("getFileStream")]
[HttpGet]
public Customer GetFileStream(Guid fileId)
{
// get the stream to return:
System.IO.Stream myStream = ...
// get the request from base class WebApi, to create an OK response
HttpResponseMessage responesMessage = this.Request.CreateResponse(HttpStatusCode.OK);
responseMessage.Content = new StreamContent(myStream);
// use extra parameters if non-default buffer size is needed
return responseMessage;
}
Client side:
public class MyOwinFileClient
{
private readonly Owin.Client.WebApiClient webApiClient;
// constructor:
public MyOwinFileClient()
{
this.webApiClient = new Owin.Client.WebApiClient(... url);
}
// the function to get the stream:
public async Task<Stream> GetFileStream(Guid fileId)
{
HttpClient myClient = ...
string requestStreamUri = #"test\GetFileStream?fileId=" + fileId.ToString("N");
HttpResponseMessage responseMessage = await httpClient.GetAsync(requestStreamUri)
.ConfigureAwait(false);
// throw exception if not Ok:
if (!response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new System.Net.Http.HttpRequestException($"{response.StatusCode} [{(int)response.StatusCode}]: {content}");
}
// if here: success: convert response as stream:
Stream stream = await response.Content.ReadAsStreamAsync()
.ConfigureAwait(false);
return stream;
}
}
Usage:
private async void Button1_clicked(object sender, EventArgs e)
{
// download the stream that contains the Bitmap:
Guid bitMapId = ...
MyOwinFileClient myClient = new MyOwinFileClient();
// get the stream:
Stream stream = await myClient.GetFileStream(bitMapId);
// assume the stream to be a bitmap:
Bitmap bmp = new Bitmap(stream);
this.pictureBox1.Image = bmp;
}
For simplicity I left out Dispose
Related
I have a controller method that gets a data from an API.
The LeaveBalanceAsync is called by an AJAX when my page loads.
public async Task<IActionResult> LeaveBalanceAsync(int empnum)
{
return await APICallAsync(empnum);
}
private async Task<IActionResult> APICallAsync(int emp)
{
//Fetch the JSON string from URL.
LeaveModel leave = new LeaveModel();
string apiUrl = $"http://myapi/{emp}";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode)
{
APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(await response.Content.ReadAsStringAsync());
leave = apiResponse.Data;
userLeave = leave;
}
//Return the Deserialized JSON object.
return Json(leave);
}
I would like to store the data that I got from that method to an object like this (as you can see above I tried to pass it into the userLeave model/object but after the LeaveBalanceAsync is done executing,the data that was captured and stored into userLeave is also gone)
public LeaveModel userLeave = new LeaveModel();
Or is there any possible ways I can do this using another method to store it into that object?
I have an API endpoint that returns file as attachment. for example if I access www.myfileservice.com/api/files/download/123 I could download the file directly. My requirement is to use this endpoint in another ASP.Net MVC project. So if the user hits www.mymvcapplication.com/File/DownloadDocument/123 it should also download the same file. Internally the action method should call the file service API and return the result as it is. This is the code I am using:
FileController.cs:
public HttpResponseMessage DownloadDocument(int Id)
{
return new DocumentClient().DownloadDocument(Id);
}
DocumentClient.cs:
public class DocumentClient
{
private string documentServiceURL = string.Empty;
private static string downloadDocumentUri = "api/files/download/";
protected HttpClient documentClient = null;
public DocumentClient()
{
documentServiceURL = "www.myfileservice.com";
documentClient = new HttpClient();
documentClient.BaseAddress = new Uri(documentServiceURL);
}
public HttpResponseMessage DownloadDocument(int Id)
{
return documentClient.GetAsync(String.Format("{0}/{1}", downloadDocumentUri, Id)).Result;
}
}
The code above is not giving any error but only printing the response in browser window(Content-Length, Content-Disposition etc). I need to download the file instead.
I think the best is to return a FileResult from your controller:
public FileResult DownloadDocument(int Id)
{
var document = new DocumentClient().DownloadDocument(Id);
//do the transformation here
//...
//I don't know what is your file's extension, please replace "application/zip" if
//needed
return File(finalResult, "application/zip", fileName);
}
I have a function that returns a Stream with data of a file being downloaded using HttpContent that is the content of a HttpResponseMessage (using Owin)
For those interested I'll add the code at the end.
The interface to get the stream is:
async Task<Stream> DownloadLogFileAsync(...);
The caller of this function wants to create a file containing the data of this Stream. According to StackOverFlow: How do I save a stream to File I should use Stream.CopyTo to save the contents of the Stream in a file. Something like this:
(simplified: not using CancellationToken)
using(Stream downloadStream = await DownloadLogFileAsync(...) )
{
using (var fileStream = System.IO.File.Create(fullFileName))
{
await downloadStream.CopyToAsync(fileStream);
}
}
Question:
Would it improve performance if the FileStream has the same buffer size as the downloadStream? How do I get the buffer size of the download stream?
End of Question
Not related to the question, only for those interested in the OWIN / ASP file download:
I have a WCF service with a function that returns data. Creation of this data takes a considerable amount of time. The size of the returned data might be huge. Therefore it is decided to split this function into two functions:
Request creation of the file. Return a unique file handle
Request a stream containing the data of the created file.
Of course my WCF service needs proper functions to cancel creation, delete the created file and do some cleanup if the file is getting old and client forgot to request deletion.
All functions can be done using WCF. The only one that needs OWIN / ASP is the request for the file stream.
class OwinFileClient
{
private const string routePrefix = "SipLoggerFile/";
private readonly WebApiClient webApiClient = new WebApiClient() {...}
// Function to download the created file:
public async Task<Stream> DownloadSipLogFileAsync(Guid fileHandle)
{
var httpClient = this.webApiClient.CreateHttpClient();
string requestStreamFile = routePrefix + #"DownloadFile?fileHandle="
+ fileHandle.ToString("N");
var response = await httpClient.GetAsync(requestStreamFile)
.ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
string content = await response
.Content.ReadAsStringAsync()
.ConfigureAwait(false);
throw new System.Net.Http.HttpRequestException(
$"{response.StatusCode} [{(int)response.StatusCode}]: {content}");
}
// if here: success: deserialize the data
return await response.Content.ReadAsStreamAsync()
.ConfigureAwait(false);
}
}
And the WebApiClient:
class WebApiClient
{
public Uri baseAddress { get; set; }
public TimeSpan Timeout { get; set; }
public ICredentials Credentials { get; set; }
public IWebProxy Proxy { get; set; }
public HttpClient CreateHttpClient()
{
return new HttpClient(CreateHttpMessageHandler())
{
BaseAddress = this.baseAddress,
Timeout = this.Timeout,
};
}
private HttpMessageHandler CreateHttpMessageHandler()
{
return new HttpClientHandler()
{
AutomaticDecompression = System.Net.DecompressionMethods.GZip
| System.Net.DecompressionMethods.Deflate,
Credentials = this.Credentials,
PreAuthenticate = this.Credentials != null,
Proxy = this.Proxy,
UseProxy = this.Proxy != null,
};
}
I have seen similar questions asked but none that seem to help me with my issue so please bear with me.
I have a WebAPI controller method that is defined as such:
[HttpPost]
[Route("")]
public HttpResponseMessage CreateMyObject(MyObjectRequest myObject)
{
MyObject o;
try
{
o = _serviceFactory.GetInstance().CreateMyObject(myObject);
}
catch (Exception ex)
{
ex.WriteToLog();
throw ApiHelper.CreateResponseException(HttpStatusCode.InternalServerError, ex);
}
var response = Request.CreateResponse(HttpStatusCode.Created, o);
var uri = Url.Link("GetMyObjectById", new { myObjectId = o.MyObjectId.ToString() });
response.Headers.Location = new Uri(uri);
return response;
}
Say, MyObject contains two properties,
public MyObject
{
public Guid MyObjectId;
public string MyObjectName
}
A client was written to call these controller methods in a WPF application. Here is the client method that is being used:
public HttpResponseMessage CreateQuote(MyObjectRequest myObject)
{
var hashtable = new Hashtable
{
{"myObject", myObject}
};
var task = GetResponse("", hashtable);
var response = task.Result;
return response;
}
protected async Task<HttpResponseMessage> GetResponse(string path, Hashtable parameters)
{
var response = await GetAsync(BuildRequestUri(path, parameters)).ConfigureAwait(false);
return response.IsSuccessStatusCode ? response : new HttpResponseMessage();
}
protected async Task<HttpResponseMessage> GetResponse(string path)
{
return await GetResponse(path, null);
}
The controller and supporting client code was not written by me and was already in the system. I am just consuming this in the WPF application. Now, I am trying to call the controller method via the client in the application and get the MyObject from the response so that I can access the MyObjectId that has been created and set. I have tried some of the other responses to similar questions but have not even seen some of the methods that are called on the response in order to get the information. Here is the first part of the call to the client that I have in the application:
var httpResponse = ApplicationService.CreateMyObject(myObjectRequest);
The application service simply injects the client into the constructor and allows me to call the CreateMyObject method. Is there any insight that can be given to me on how I should be getting the MyObject object out of the response?
I'm still a little new to web api as well, but I'm currently working with it on a project. Give the following code a try:
MyObject myObject;
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
myObject = response.Content.ReadAsAsync<MyObject>().Result;
}
So you could theoretically change your method like this (may not be exactly what you want):
public MyObject CreateQuote(MyObjectRequest myObject)
{
var hashtable = new Hashtable
{
{"myObject", myObject}
};
var task = GetResponse("", hashtable);
var response = task.Result;
MyObject newObject;
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
newObject= response.Content.ReadAsAsync<MyObject>().Result;
}
return newObject; // instead of response
}
I'd like to verify if this is the correct usage from WebAPI perspective of an asynchronous consumption of a data set.
Similar implementation tha creates an IQueryable<T> extension can be seen here.
My GetByProcedureAsync() uses the idea to create SqlDataReader like this:
var result = await System.Threading.Tasks.Task.Factory.FromAsync<SqlDataReader>(cmd.BeginExecuteReader(), cmd.EndExecuteReader);
I need to consume this in a MVC WebApi controller action. I don't know enough to quickly judge if I am hampering the async nature of the design.
Do I do public HttpResponseMessage Get([FromUri] int key) or public async Task<HttpResponseMessage> Get([FromUri] int key) in this:
public HttpResponseMessage Get([FromUri] int key)
{
HttpResponseMessage response = Request.CreateResponse();
response.Content = new PushStreamContent(async (stream, content, context) =>
{
try
{
var set = await MyRepo.GetByProcedureAsync("PROC_NAME", key);
// set is IEnumerable<MyEntity>
foreach (MyEntity p in set)
{
var buffer = System.Text.UTF8Encoding.UTF8.GetBytes(p.ToString());
await stream.WriteAsync(buffer, 0, buffer.Length);
}
}
finally
{
// Close output stream as we are done
stream.Close();
}
});
return response;
}
Note that underlying sproc is observing the requirements to NOT push anything else to client before relevant record set.