Getting a Stream from an absolute path? - c#

I have this method:
public RasImage Load(Stream stream);
if I want to load a url like:
string _url = "http://localhost/Application1/Images/Icons/hand.jpg";
How can I make this url in to a stream and pass it into my load method?

Here's one way. I don't really know if it's the best way or not, but it works.
// requires System.Net namespace
WebRequest request = WebRequest.Create(_url);
using (var response = request.GetRespone())
using (var stream = response.GetResponseStream())
{
RasImage image = Load(stream);
}
UPDATE: It looks like in Silverlight, the WebRequest class has no GetResponse method; you've no choice but to do this asynchronously.
Below is some sample code illustrating how you might go about this. (I warn you: I wrote this just now, without putting much thought into how sensible it is. How you choose to implement this functionality would likely be quite different. Anyway, this should at least give you a general idea of what you need to do.)
WebRequest request = WebRequest.Create(_url);
IAsyncResult getResponseResult = request.BeginGetResponse(
result =>
{
using (var response = request.EndGetResponse(result))
using (var stream = response.GetResponseStream())
{
RasImage image = Load(stream);
// Do something with image.
}
},
null
);
Console.WriteLine("Waiting for response from '{0}'...", _url);
getResponseResult.AsyncWaitHandle.WaitOne();
Console.WriteLine("The stream has been loaded. Press Enter to quit.");
Console.ReadLine();

Dan's answer is a good one, though you're requesting from localhost. Is this a file you can access from the filesystem? If so, I think you should be able to just pass in a FileStream:
FileStream stream = new FileStream(#"\path\to\file", FileMode.Open);

Related

Unable to create System.Drawing.Image object from WebResponse Stream

I am trying to create a System.Drawing.Image object from a WebResponse's response stream. Here's what I'm doing:
using (WebResponse response = await request.GetResponseAsync())
{
using (Stream originalInputStream = response.GetResponseStream())
{
// some code that calls a 3rd party image resizer, passing in the original stream
ResizeImage(originalInputStream, out resizedOutputStream);
// manipulation of the resizedOutputStream
// now i want to create an image from the ORIGINAL stream
// ERROR HAPPENS HERE!!
using (Image image = Image.FromStream(originalInputStream))
{
// random code that doesn't get hit because of the error above
}
}
}
When the program tries to create the Image.FromStream() in the using statement, I get an error saying:
'Parameter is not valid.'
I assume this is because of the manipulation of the originalInputStream in the resizer function. I searched around and found that resetting the position of the stream can solve these issues. So I tried that, using both:
originalInputStream.Seek(0, SeekOrigin.Begin);
originalInputStream.Position = 0;
but both of those are erroring out as well, giving me an error message of:
Specified method is not supported.
When I try and create the Image.FromStream(), WITHOUT any of the preceding image resizing/stream manipulation... IT WORKS. But THEN, I cannot do anything else with the stream afterwards, or else it will error out as before. However, I need to do the manipulation, and also create the Image, so I'm stuck.
Is the best course of action to simply create another request? And just create the image from the new request's response stream? that just seems like bad practice, I don't know, I feel like what I'm doing should theoretically work and I'm probably just doing something dumb.
Thanks in advance for the help, let me know if I can provide any extra info.
Resetting the stream position does not work on the Stream base class. If you can read the response into a MemoryStream, then you will be able to manipulate the image as you please. Something like this could work:
using (WebResponse response = await request.GetResponseAsync())
{
using (Stream responseStream = response.GetResponseStream())
{
using (MemoryStream memStream = new MemoryStream())
{
responseStream.CopyTo(memStream);
// some code that calls a 3rd party image resizer, passing in the original stream
ResizeImage(memStream, out resizedOutputStream);
memStream.Seek(0, SeekOrigin.Begin);
// manipulation of the resizedOutputStream
// now i want to create an image from the ORIGINAL stream
using (Image image = Image.FromStream(memStream))
{
}
}
}
}

Unable to read input stream

I am using ActionFilterAttribute to get the request before hitting the controller as below :
public override void OnActionExecuting(HttpActionContext actionContext)
{
using (var stream = new MemoryStream())
{
HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
context.Request.InputStream.Seek(0, SeekOrigin.Begin);
context.Request.InputStream.CopyTo(stream);
requestBody = Encoding.UTF8.GetString(stream.ToArray());
}
}
The above method is working for small request but for a large json it is giving me this error :
Either BinaryRead, Form, Files, or InputStream was accessed before the internal storage was filled by the caller of HttpRequest.GetBufferedInputStream.
And the input stream gives this error
context.Request.InputStream threw an exception of type System.InvalidOperationException System.IO.Stream {System.InvalidOperationException}
As I found in my research that it is an issue with the timeout but I am unable to change the timeout in the code. I tried changing the values in the web.config file maxRequestLength="102400000" and maxAllowedContentLength="209715100" but still I am facing the same error.
If I read the GetBufferedInputStream but still same issue it is reading just a part of the buffer, not the entire stream.
I also tried the below :
Stream InStream;
int Len;
InStream = HttpContext.Current.Request.InputStream;
Len = System.Convert.ToInt32(InStream.Length);
byte[] ByteArray = new byte[Len + 1];
InStream.Seek(0, SeekOrigin.Begin);
InStream.Read(ByteArray, 0, Len);
var jsonParam = System.Text.Encoding.UTF8.GetString(ByteArray);
Note that if I set the content type application/xml or application/x-www-form-urlencoded it works, but if I set it to application/json it gives me this error!!
Please advise!
There are couple of points:
First, if you try and read 0 bytes from a stream, then it will throw a System.InvalidOperationException exception. So, I will change your code like below and add a check for ContentLength > 0.
using (var stream = new MemoryStream())
{
HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
if(context.Request.Contentlength > 0)
{
context.Request.InputStream.Seek(0, SeekOrigin.Begin);
context.Request.InputStream.CopyTo(stream);
requestBody = Encoding.UTF8.GetString(stream.ToArray());
}
}
Also, I once experienced the same issue and increasing the maxRequestLength in web.config seems to have resolved the issue.
This link further provides more info here
This is how I do it inside my Model binder but I'm not sure how it will work with your Action filter. I checked online and there's conflicting information; Some say you cannot read the input stream since it's not seekable and ASP.NET would need to read it to bind the model. Some say it's indeed seekable, and use the method you shared above. So the only way to figure out what would really work is to test.
I hope my code sample helps you figure it out.
object request = null;
if (actionContext.Request.Method == HttpMethod.Post && "application/json".Equals(actionContext.Request.Content.Headers.ContentType.MediaType))
{
var jsonContentTask = actionContext.Request.Content.ReadAsStringAsync();
Task.WaitAll(jsonContentTask);
string jsonContent = jsonContentTask.Result;
//... other stuff
}
I may be wrong but here is what I found. The action filters are executed after the model binding, which means the request stream has already been read. In your case i am not sure what that means. https://exceptionnotfound.net/the-asp-net-web-api-2-http-message-lifecycle-in-43-easy-steps-2/ explains the lifecycle in detail. Changing the content type wouldn't change the lifecycle events but would rather change the request content which in turn might affect the model binding.
If you have a model set for the action, then How to get current model in action filter should help. So the solution would be to get model object from the actionContext and then modify it accordingly.

Stream video from RavenDB through ASP.Net MVC

I'm trying to stream a Video, that is saved as an attachment in a Ravendb-Database, through an ASP.NET MVC 5 Action to a WebBrowser. It is working with the following Code, but the Video gets fully downloaded before the Video starts. I don't get what I'm doing wrong.
I found some ways to do Streaming in MVC, but they seem to expect a Seekable Stream - but the stream I receive from Ravendb is not seekable; it even does not provide a length. So the only way of doing it would be to copy the ravendb-stream to a memorystream and provide a PartialContent or similar from there.
Does anybody have a better solution? I cannot be the only one that wants to stream a Video from a database without loading the full Video into Memory before sending it.
I'm fetching the attachment from ravendb like this:
public async Task<System.IO.Stream> GetAttachmentAsync(IAttachmentPossible attachedObject, string key)
{
using (var ds = InitDatabase())
{
using (var session = ds.OpenAsyncSession())
{
try
{
var result = await session.Advanced.Attachments.GetAsync(attachedObject.Id, key);
return result.Stream;
}
catch (Exception ex)
{
return null;
}
}
}
}
After that I send the stream to the browser like this:
var file = await _database.GetAttachmentAsync(entry, attachmentId);
HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.OK);
msg.Content = new StreamContent(file);
msg.Content.Headers.ContentType = new MediaTypeHeaderValue("video/mp4");
return msg;
Any ideas? Thank you very much!
I think that it is correct to copy the stream into a memory stream. With the answer you linked (https://stackoverflow.com/a/39247028/10291808) you can do streaming.
Maybe could be an idea to think about the multiple calls that will done to retrieve the file from the db (maybe you could cache the file for a limited time to improve performance).

Is it safe to return a FileStream in a method in C#?

In ASP.NET WebForms 4.5, I have WebAPI Controller with a GET method for getting a PDF.
Then down in the business layer of the application, I have an API class with a method that contains the logic for actually finding and returning the PDF to the controller.
So MyController class basically has:
public HttpResponseMessage GetStatement(string acctNumber, string stmtDate) {
MyApi myApi = new MyApi();
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
FileStream stream = myApi.GetStatement(acctNumber, stmtDate);
...set the response.Content = stream...
... set the mime type..
... close the stream...
return response;
}
And MyApi class has:
public FileStream GetStatement(string acctNumber, string stmtDate) {
... makes an HttpWebRequest to get a PDF from another system ...
HttpWebRequest req = WebRequest.Create(......)....
FileStream stream = new FileStream(accountNumber +"_" + stmtDate + ".pdf", FileMode.Create);
response.GetResponseStream().CopyTo(stream);
return stream;
}
The API class is not in the web layer of the application because it's used by other (non-web) parts of the software.
I guess my concern is there's no explicit closing of the FileStream in the API method. I could do it in the Controller method, but I'd be relying on others to do the same when they're calling it from other areas.
Is there a better way to return the PDF file from the API method? Possibly just as a byte array or something like that? Preferably as little overhead as possible.
Thanks-
You should not be returning a file stream but instead an array of bytes. This way you can correctly dispose of the object correctly and not worry about other calling methods in the stack.
byte[] currentFile = ....
You can then deliver your file as follows this, a byte array is easy to convert to anything. Below example is for MVC4.
return new FileContentResult(currentFile, "application/pdf");
It's not uncommon for methods to return FileStreams, hoping that the caller will remember to put the method call in a using statement. But it's understandable to not want to make that assumption. One alternative is to use an interesting form of Inversion of Control, where you require the caller to give you the callback function that knows what to do with the FileStream, and then you wrap a call to that handler inside a using statement.
public T GetStatement<T>(string acctNumber, string stmtDate,
Func<FileStream, T> callback) {
... makes an HttpWebRequest to get a PDF from another system ...
HttpWebRequest req = WebRequest.Create(......)....
using(FileStream stream = new FileStream(accountNumber +"_" + stmtDate + ".pdf", FileMode.Create))
{
response.GetResponseStream().CopyTo(stream);
return callback(stream);
}
}
However, this is going to require a little extra hackery in your use case because you're returning a response whose content is feeding through the stream, so you'd need to find a way to cause your message to push out the entire response before the callback returns.
It may be best in this case to just throw comments on your method to document the fact that you're expecting the caller to ensure the stream gets closed. That's what we do in our application, and it's worked well so far.
Generally, you should put your FileStream inside a using block as already described by other answers or have some faith that it will be disposed of by other parts of your code. However, this is tricky when you are returning a FileStream from your controller. For example:
public ActionResult GetImage()
{
Stream stream = //get the stream
return base.File( stream, "image/png" );
}
Happily, the stream is disposed of by the framework once written, so you don't need to worry about the disposing of it. See here for details.
This is not an answer to your exact requirement, but thought of sharing it so that it can give you an idea of other ways of doing this.
public async Task<HttpResponseMessage> Get(string acctNumber, string stmtDate)
{
HttpResponseMessage response2 = new HttpResponseMessage();
HttpClient client= new HttpClient();
string url = "http://localhost:9090/BusinessLayer?acctNumber=" + acctNumber + "&stmtDate=" + stmtDate;
// NOTE 1: here we are only reading the response1's headers and not the body. The body of response1 would be later be
// read by response2.
HttpResponseMessage response1 = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
// NOTE 2: here we need not close response1's stream as Web API would close it when disposing
// response2's stream content.
response2.Content = new StreamContent(await response1.Content.ReadAsStreamAsync());
response2.Content.Headers.ContentLength = response1.Content.Headers.ContentLength;
response2.Content.Headers.ContentType = response1.Content.Headers.ContentType;
return response2;
}

How do I download a large file (via HTTP) in .NET?

I need to download a large file (2 GB) over HTTP in a C# console application. Problem is, after about 1.2 GB, the application runs out of memory.
Here's the code I'm using:
WebClient request = new WebClient();
request.Credentials = new NetworkCredential(username, password);
byte[] fileData = request.DownloadData(baseURL + fName);
As you can see... I'm reading the file directly into memory. I'm pretty sure I could solve this if I were to read the data back from HTTP in chunks and write it to a file on disk.
How could I do this?
If you use WebClient.DownloadFile you could save it directly into a file.
The WebClient class is the one for simplified scenarios. Once you get past simple scenarios (and you have), you'll have to fall back a bit and use WebRequest.
With WebRequest, you'll have access to the response stream, and you'll be able to loop over it, reading a bit and writing a bit, until you're done.
From the Microsoft documentation:
We don't recommend that you use WebRequest or its derived classes for
new development. Instead, use the System.Net.Http.HttpClient class.
Source: learn.microsoft.com/WebRequest
Example:
public void MyDownloadFile(Uri url, string outputFilePath)
{
const int BUFFER_SIZE = 16 * 1024;
using (var outputFileStream = File.Create(outputFilePath, BUFFER_SIZE))
{
var req = WebRequest.Create(url);
using (var response = req.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
var buffer = new byte[BUFFER_SIZE];
int bytesRead;
do
{
bytesRead = responseStream.Read(buffer, 0, BUFFER_SIZE);
outputFileStream.Write(buffer, 0, bytesRead);
} while (bytesRead > 0);
}
}
}
}
Note that if WebClient.DownloadFile works, then I'd call it the best solution. I wrote the above before the "DownloadFile" answer was posted. I also wrote it way too early in the morning, so a grain of salt (and testing) may be required.
You need to get the response stream and then read in blocks, writing each block to a file to allow memory to be reused.
As you have written it, the whole response, all 2GB, needs to be in memory. Even on a 64bit system that will hit the 2GB limit for a single .NET object.
Update: easier option. Get WebClient to do the work for you: with its DownloadFile method which will put the data directly into a file.
WebClient.OpenRead returns a Stream, just use Read to loop over the contents, so the data is not buffered in memory but can be written in blocks to a file.
i would use something like this
The connection can be interrupted, so it is better to download the file in small chunks.
Akka streams can help download file in small chunks from a System.IO.Stream using multithreading. https://getakka.net/articles/intro/what-is-akka.html
The Download method will append the bytes to the file starting with long fileStart. If the file does not exist, fileStart value must be 0.
using Akka.Actor;
using Akka.IO;
using Akka.Streams;
using Akka.Streams.Dsl;
using Akka.Streams.IO;
private static Sink<ByteString, Task<IOResult>> FileSink(string filename)
{
return Flow.Create<ByteString>()
.ToMaterialized(FileIO.ToFile(new FileInfo(filename), FileMode.Append), Keep.Right);
}
private async Task Download(string path, Uri uri, long fileStart)
{
using (var system = ActorSystem.Create("system"))
using (var materializer = system.Materializer())
{
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
request.AddRange(fileStart);
using (WebResponse response = request.GetResponse())
{
Stream stream = response.GetResponseStream();
await StreamConverters.FromInputStream(() => stream, chunkSize: 1024)
.RunWith(FileSink(path), materializer);
}
}
}

Categories

Resources