I am trying to save and retrieve an Image from HTTPRuntime Cache but I am getting an exception. I am able to save a stream to cache but when I try to retrieve it I get an exception saying:
the request was aborted. The connection was closed unexpectedly
Here is my code:
public void ProcessRequest(HttpContext context)
{
string courseKey = context.Request.QueryString["ck"];
string objKey = context.Request.QueryString["file"];
if(HttpRuntime.Cache[objKey] !=null)
{
using (Stream stream = (Stream)HttpRuntime.Cache[objKey]) // here is where I get an exception
{
var buffer = new byte[8000];
var bytesRead = -1;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
context.Response.OutputStream.Write(buffer, 0, bytesRead);
}
}
return;
}
var response = Gets3Response(objKey, courseKey, context);
if (response != null)
{
using (response)
{
var MIMEtype = response.ContentType;
context.Response.ContentType = MIMEtype;
var cacheControl = context.Response.CacheControl;
HttpRuntime.Cache.Insert(objKey, response.ResponseStream, null, DateTime.UtcNow.AddMinutes(20), Cache.NoSlidingExpiration);
using (Stream responseStream = response.ResponseStream)
{
var buffer = new byte[8000];
var bytesRead = -1;
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
context.Response.OutputStream.Write(buffer, 0, bytesRead);
}
}
}
}
}
And here is the exception I am getting
This code is pretty confusing. First, you are caching response.ResponseStream, but that has been wrapped in a using block. So by the time you get to HttpRuntime.Cache.Insert, response.ResponseStream is already disposed and closed. Hence the error.
You should not be caching a stream. For one thing, once you put a distributed cache service in place, your approach will be impossible. You need to refactor this. Consider:
public class CacheAsset
{
public string FileName { get; set; }
public string ContentType { get; set; }
public byte[] Content { get; set; }
}
CacheAsset GetAsset(HttpContext context)
{
string courseKey = context.Request.QueryString["ck"];
string objKey = context.Request.QueryString["file"];
var asset = context.Cache[objKey] as CacheAsset;
if (asset != null) return asset;
using (var response = Gets3Response(objKey, courseKey, context))
using (var stream = new MemoryStream())
{
var buffer = new byte[8000];
var read = 0;
while ((read = response.ReponseStream.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, read);
}
asset = new CacheAsset
{
FileName = objKey,
ContentType = reponse.ContentType,
Content = stream.ToArray()
};
context.Cache.Insert(objKey, asset, null, DateTime.UtcNow.AddMinutes(20), Cache.NoSlidingExpiration);
}
return asset;
}
public void ProcessRequest(HttpContext context)
{
var asset = GetAsset(context);
context.Response.ContentType = asset.ContentType;
context.Response.BinaryWrite(asset.Content);
}
Related
i want to stream video from database through asp.net web api controller. i have done it from static file in my server(below code), but i can't accomplish the database mode.
here is my code (which i got from searching through web)
public class VideosController : ApiController
{
// GET api/values
public HttpResponseMessage Get(string filename)
{
var filePath = HttpContext.Current.Server.MapPath("~") + filename;
if (!File.Exists(filePath))
return new HttpResponseMessage(HttpStatusCode.NotFound);
var response = Request.CreateResponse();
response.Headers.AcceptRanges.Add("bytes");
var streamer = new FileStreamer();
streamer.FileInfo = new FileInfo(filePath);
response.Content = new PushStreamContent(streamer.WriteToStream, "video/mp4");
RangeHeaderValue rangeHeader = Request.Headers.Range;
if (rangeHeader != null)
{
long totalLength = streamer.FileInfo.Length;
var range = rangeHeader.Ranges.First();
streamer.Start = range.From ?? 0;
streamer.End = range.To ?? totalLength - 1;
response.Content.Headers.ContentLength = streamer.End - streamer.Start + 1;
response.Content.Headers.ContentRange = new ContentRangeHeaderValue(streamer.Start, streamer.End,
totalLength);
response.StatusCode = HttpStatusCode.PartialContent;
}
else
{
response.StatusCode = HttpStatusCode.OK;
}
return response;
}
class FileStreamer
{
public FileInfo FileInfo { get; set; }
public long Start { get; set; }
public long End { get; set; }
public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[65536];
using (var video = FileInfo.OpenRead())
{
if (End == -1)
{
End = video.Length;
}
var position = Start;
var bytesLeft = End - Start + 1;
video.Position = Start;
while (position <= End)
{
// what should i do here?
var bytesRead = video.Read(buffer, 0, (int)Math.Min(bytesLeft, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
position += bytesRead;
bytesLeft = End - position + 1;
}
}
}
catch (Exception ex)
{
// fail silently
}
finally
{
outputStream.Close();
}
}
}
}
this is my HTML code:
<video width="640" height="480" controls="controls">
<source src="/api/Videos/?filename=sample.mp4" type="video/mp4">
</video>
there is a method ready for me (written by someone else) to get a range of file from engine (database) and its code is like this :
public byte[] Engine.DownloadStreamFile(Guid fileId, long from, long length)
i tried to read from this method and write on response output stream, but i couldn't. it seems i can't handle From and To receiving from google chrome. Any thoughts?
Based on the information you've provided, and assuming the method in your Engine class does what one would assume it does by name and signature, you should try replacing the file system stuff with your Engine.DownloadStreamFile method:
// what should i do here?
var bytesRead = video.Read(buffer, 0, (int)Math.Min(bytesLeft, buffer.Length));
// becomes
var bytesRead = Engine.DownloadStreamFile(this.fileId, this.Start, this.End);
You will obviously need to add a fileId field/property to your class instead of the FileInfo you have today.
I'm looking for your expertise/help figuring out why my http handler works perfectly over http and doesn't over https (only IE problem, other browsers work fine either way). I'm using the handler to display images and when https is used on IE, it shows some images and fails to show others images.
again, the problem is only happening on https and IE, all other mutations with other browsers work just fine. below is my code:
public void ProcessRequest(HttpContext context)
{
Stream iStream = null;
try
{
if (!context.Response.IsClientConnected || HttpContext.Current == null || HttpContext.Current.Session["GoodUser"] == null)
return;
var url = context.Request.QueryString[#"ID"];
if (string.IsNullOrWhiteSpace(url))
return;
var fileUrl = EncryptionManager.DecryptUrl(url);
if (!File.Exists(fileUrl))
return;
var buffer = new byte[4096];
// Open the file.
iStream = new FileStream(fileUrl, FileMode.Open, FileAccess.Read, FileShare.Read);
// Total bytes to read:
var dataToRead = iStream.Length;
context.Response.AddHeader(#"Accept-Ranges", #"bytes");
context.Response.AddHeader(#"Content-Length", dataToRead.ToString());
context.Response.StatusCode= (int)HttpStatusCode.OK;
var file = new FileInfo(fileUrl);
context.Response.ContentType = MimeTypeMap.GetMimeType(file.Extension);
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Cache.SetNoStore();
context.Response.Cache.SetExpires(DateTime.MinValue);
if (!string.IsNullOrEmpty(context.Request.Headers[#"Range"]))
{
var range = context.Request.Headers[#"Range"].Split('=', '-');
var startbyte = int.Parse(range[1]);
iStream.Seek(startbyte, SeekOrigin.Begin);
context.Response.StatusCode = 206;
context.Response.AddHeader(#"Content-Range", $#" bytes {startbyte}-{dataToRead - 1}/{dataToRead}");
}
while (dataToRead > 0)
{
iStream.Read(buffer, 0, buffer.Length);
if (context.Response.IsClientConnected)
{
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.Flush();
buffer = new byte[buffer.Length];
dataToRead = dataToRead - buffer.Length;
}
else
{
dataToRead = -1;
}
}
}
catch (Exception exception)
{
}
finally
{
iStream?.Close();
context.ApplicationInstance.CompleteRequest();
}
}
I try stream audios with PushStreamContent and WebApi.
For this I coded something. Main code;
[HttpGet]
public HttpResponseMessage StreamCall(long callId,int playSpeed)
{
var audio = new AudioStreamHelper();
var response = Request.CreateResponse();
response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>) audio.WriteToStream, new MediaTypeHeaderValue("audio/wav"));
return response;
}
AudioStreamHelper code;
public class AudioStreamHelper
{
private readonly string _filename;
protected static readonly ILogger Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.Name);
public AudioStreamHelper()
{
_filename = #"C:/195545.mp3";
}
public async void WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[12365536];
using (var audio = File.Open(_filename, FileMode.Open, FileAccess.Read,FileShare.ReadWrite))
{
var length = (int)audio.Length;
var bytesRead = 1;
while (length > 0 && bytesRead > 0)
{
bytesRead = audio.Read(buffer, 0, Math.Min(length, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
length -= bytesRead;
}
}
}
catch (HttpException ex)
{
Logger.Error(ex.Message);
throw new UserFriendlyException(AppTexts.AudioStreamError);
}
finally
{
outputStream.Close();
}
}
}
When i debug this code; In AudioStreamHelper class WriteToStream method and await outputStream.WriteAsync(buffer, 0, bytesRead);line, my thread is locking/freezing and gives no response.I check current thread Id. It's same.(In Api and Helper class) Also, this code works another demo application with System.Net.Http.Formattion(version 4.0.0.0)
My project use System.Net.Http.Formattion(version 5.1.0.0)
Is associated with this condition ? I don't understand this situation.
I have some code which downloads a file from a remote URL and saves it quite efficiently.
How can I modify this to set the height to be 120px (and width scale accordingly) when I save it?
private static bool DownloadRemoteImageFile(string uri, string fileName)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
HttpWebResponse response;
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (Exception)
{
return false;
}
if ((response.StatusCode == HttpStatusCode.OK ||
response.StatusCode == HttpStatusCode.Moved ||
response.StatusCode == HttpStatusCode.Redirect) &&
response.ContentType.StartsWith("image", StringComparison.OrdinalIgnoreCase))
{
using (Stream inputStream = response.GetResponseStream())
using (Stream outputStream = System.IO.File.OpenWrite(fileName))
{
var buffer = new byte[4096];
int bytesRead;
do
{
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
outputStream.Write(buffer, 0, bytesRead);
} while (bytesRead != 0);
}
return true;
}
return false;
}
The examples I've seen use the Bitmap class which I'm not using, do I need to change how I download my file?
Update your DownloadRemoteImageFile method to return a byte[]:
private static byte[] DownloadRemoteImageFile(string uri)
{
byte[] content;
var request = (HttpWebRequest)WebRequest.Create(uri);
using (var response = request.GetResponse())
using (var reader = new BinaryReader(response.GetResponseStream()))
{
content = reader.ReadBytes(100000);
}
return content;
}
Then use System.Drawing classes to re-size and save the image:
static void Main(string[] args)
{
var img = DownloadRemoteImageFile( // Put your url here );
Image original;
using (var ms = new MemoryStream(img))
{
original = Image.FromStream(ms);
}
var newHeight = 120;
var newWidth = ScaleWidth(original.Height, 120, original.Width);
using (var newPic = new Bitmap(newWidth, newHeight))
using (var gr = Graphics.FromImage(newPic))
{
gr.DrawImage(original, 0, 0, newWidth, newHeight);
newPic.Save(#"C:\newImage1.jpg", ImageFormat.Jpeg);
}
}
To scale the width:
private static int ScaleWidth(int originalHeight, int newHeight, int originalWidth)
{
var scale = Convert.ToDouble(newHeight) / Convert.ToDouble(originalHeight);
return Convert.ToInt32(originalWidth * scale);
}
I'm trying to replace this:
void ProcessRequest(object listenerContext)
{
var context = (HttpListenerContext)listenerContext;
Uri URL = new Uri(context.Request.RawUrl);
HttpWebRequest.DefaultWebProxy = null;
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(URL);
httpWebRequest.Method = context.Request.HttpMethod;
httpWebRequest.Headers.Clear();
if (context.Request.UserAgent != null) httpWebRequest.UserAgent = context.Request.UserAgent;
foreach (string headerKey in context.Request.Headers.AllKeys)
{
try { httpWebRequest.Headers.Set(headerKey, context.Request.Headers[headerKey]); }
catch (Exception) { }
}
using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse())
{
Stream responseStream = httpWebResponse.GetResponseStream();
if (httpWebResponse.ContentEncoding.ToLower().Contains("gzip"))
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
else if (httpWebResponse.ContentEncoding.ToLower().Contains("deflate"))
responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
MemoryStream memStream = new MemoryStream();
byte[] respBuffer = new byte[4096];
try
{
int bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
while (bytesRead > 0)
{
memStream.Write(respBuffer, 0, bytesRead);
bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
}
}
finally
{
responseStream.Close();
}
byte[] msg = memStream.ToArray();
context.Response.ContentLength64 = msg.Length;
using (Stream strOut = context.Response.OutputStream)
{
strOut.Write(msg, 0, msg.Length);
}
}
catch (Exception ex)
{
// Some error handling
}
}
with sockets. This is what I have so far:
void ProcessRequest(object listenerContext)
{
HttpListenerContext context = (HttpListenerContext)listenerContext;
Uri URL = new Uri(context.Request.RawUrl);
string getString = string.Format("GET {0} HTTP/1.1\r\nHost: {1}\r\nAccept-Encoding: gzip\r\n\r\n",
context.Request.Url.PathAndQuery,
context.Request.UserHostName);
Socket socket = null;
string[] hostAndPort;
if (context.Request.UserHostName.Contains(":"))
{
hostAndPort = context.Request.UserHostName.Split(':');
}
else
{
hostAndPort = new string[] { context.Request.UserHostName, "80" };
}
IPHostEntry ipAddress = Dns.GetHostEntry(hostAndPort[0]);
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(ipAddress.AddressList[0].ToString()), int.Parse(hostAndPort[1]));
socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ip);
BEGIN NEW CODE
Encoding ASCII = Encoding.ASCII;
Byte[] byteGetString = ASCII.GetBytes(getString);
Byte[] receiveByte = new Byte[256];
string response = string.Empty;
socket.Send(byteGetString, byteGetString.Length, 0);
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
response += ASCII.GetString(receiveByte, 0, bytes);
while (bytes > 0)
{
bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);
}
socket.Close();
string separator = "\r\n\r\n";
string header = strPage.Substring(0,strPage.IndexOf(separator));
string content = strPage.Remove(0, strPage.IndexOf(separator) + 4);
byte[] byteResponse = ASCII.GetBytes(content);
context.Response.ContentLength64 = byteResponse .Length;
context.Response.OutputStream.Write(byteResponse , 0, byteResponse .Length);
context.Response.OutputStream.Close();
END NEW CODE
After connecting to the socket I don't know how to get the Stream response to decompress, and send back to context.Response.OutputStream
Any help will be appreciated.
Thanks.
Cheers.
EDIT 2:
With this edit now seems to be working fine (same as HttpWebRequest at least). Do you find any error here?
EDIT 3:
False alarm... Still can't get this working
EDIT 4:
I needed to add the following lines to Scott's code ... because not always the first to bytes of reponseStream are the gzip magic number.
The sequence seems to be: 0x0a (10), 0x1f (31), 0x8b (139). The last two are the gzip magic number. The first number was always before in my tests.
if (contentEncoding.Equals("gzip"))
{
int magicNumber = 0;
while (magicNumber != 10)
magicNumber = responseStream.ReadByte();
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
}
Here's some code that works for me.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.IO.Compression;
namespace HttpUsingSockets {
public class Program {
private static readonly Encoding DefaultEncoding = Encoding.ASCII;
private static readonly byte[] LineTerminator = new byte[] { 13, 10 };
public static void Main(string[] args) {
var host = "stackoverflow.com";
var url = "/questions/523930/sockets-in-c-how-to-get-the-response-stream";
IPHostEntry ipAddress = Dns.GetHostEntry(host);
var ip = new IPEndPoint(ipAddress.AddressList[0], 80);
using (var socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) {
socket.Connect(ip);
using (var n = new NetworkStream(socket)) {
SendRequest(n, new[] {"GET " + url + " HTTP/1.1", "Host: " + host, "Connection: Close", "Accept-Encoding: gzip"});
var headers = new Dictionary<string, string>();
while (true) {
var line = ReadLine(n);
if (line.Length == 0) {
break;
}
int index = line.IndexOf(':');
headers.Add(line.Substring(0, index), line.Substring(index + 2));
}
string contentEncoding;
if (headers.TryGetValue("Content-Encoding", out contentEncoding)) {
Stream responseStream = n;
if (contentEncoding.Equals("gzip")) {
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
}
else if (contentEncoding.Equals("deflate")) {
responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
}
var memStream = new MemoryStream();
var respBuffer = new byte[4096];
try {
int bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
while (bytesRead > 0) {
memStream.Write(respBuffer, 0, bytesRead);
bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
}
}
finally {
responseStream.Close();
}
var body = DefaultEncoding.GetString(memStream.ToArray());
Console.WriteLine(body);
}
else {
while (true) {
var line = ReadLine(n);
if (line == null) {
break;
}
Console.WriteLine(line);
}
}
}
}
}
static void SendRequest(Stream stream, IEnumerable<string> request) {
foreach (var r in request) {
var data = DefaultEncoding.GetBytes(r);
stream.Write(data, 0, data.Length);
stream.Write(LineTerminator, 0, 2);
}
stream.Write(LineTerminator, 0, 2);
// Eat response
var response = ReadLine(stream);
}
static string ReadLine(Stream stream) {
var lineBuffer = new List<byte>();
while (true) {
int b = stream.ReadByte();
if (b == -1) {
return null;
}
if (b == 10) {
break;
}
if (b != 13) {
lineBuffer.Add((byte)b);
}
}
return DefaultEncoding.GetString(lineBuffer.ToArray());
}
}
}
You could substitute this for the Socket/NetworkStream and save a bit of work.
using (var client = new TcpClient(host, 80)) {
using (var n = client.GetStream()) {
}
}
Socket, by definition, is the low level to access the network. You can even use datagram protocols with a socket. In that case a stream does not make sense at all.
While I'm not sure why are you doing what HttpWebRequest easily accomplishes, to read/write data to a socket, you use the Send/Receive methods. If you want to have a stream like access to a TCP socket, you should use the TcpClient/TcpListener classes which wrap a socket and provide a network stream for it.
There's also the NetworkStream class that takes a Socket as a parameter. ;)