InMemoryRandomAccessStream incorrect(?) behavior - c#

I have 2 methods with identical functionality:
private static async Task<IRandomAccessStream> _DownloadImage(string url)
{
HttpClient http = new HttpClient();
var httpStream = await http.GetStreamAsync(url);
var memStream = new MemoryStream();
await httpStream.CopyToAsync(memStream);
memStream.Seek(0, SeekOrigin.Begin);
return memStream.AsRandomAccessStream();
}
and
private static async Task<IRandomAccessStream> _DownloadImage2(string url)
{
HttpClient http = new HttpClient();
var httpStream = await http.GetStreamAsync(url);
var stream = new InMemoryRandomAccessStream();
await httpStream.CopyToAsync(stream.AsStreamForWrite());
stream.Seek(0);
return stream;
}
The problem is that for some URLs the second version gives a zero-length stream. Is it a bug or am I missing something?
The url for which the first version gives a normal stream with picture while the second one is empty: http://icdn.lenta.ru/images/2014/09/17/10/20140917102331786/pic_ffa73ea27023b3e69b6cef4d068c0499.jpg
UPD:
I've found that files which have size below 16384 are at risk. I found this 16384 value at the AsStreamForWrite documentation.
#Kiewic, also proposed a workaround. Still I'd like to know why it doesn't work as is.
UPD2:
Created a bug report in Connect.

It works when you keep the Stream in a variable, try:
// using System.Net.Http;
private static async Task<IRandomAccessStream> _DownloadImage2(string url)
{
HttpClient http = new HttpClient();
var httpStream = await http.GetStreamAsync(url);
var stream = new InMemoryRandomAccessStream();
Stream streamForWrite = stream.AsStreamForWrite();
await httpStream.CopyToAsync(streamForWrite);
stream.Seek(0);
Debug.WriteLine("Length: " + streamForWrite.Length);
Debug.WriteLine("Size: " + stream.Size);
return stream;
}

Related

How to return PDF content from another HttpResponseMessage to the browser?

I am in the process of creating a proxy server that makes a request to a PDF Blob link then takes the request to setup its HttpResponse Header which we sent to the client. This diagram should explain
As of now, I am successful at making the request to get the pdf content however I am not sure how to send that back to the user. I have followed other Stackoverflow post such as this one : https://stackoverflow.com/a/43232581/10541061
I turn the response message in step 3 of the diagram to a stream and sent it back in the new HttpResponseMessage content.But instead of PDF content , I get a json file
What I want to return to the client
What I am actually returning to the client
Here is the code I am using to create this proxy endpoint
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<HttpResponseMessage> OpenPDF([FromQuery] string url)
{
var _httpClient = _httpClientFactory.CreateClient();
var response = await _httpClient.GetAsync(url);
var stream = await response.Content.ReadAsStreamAsync();
HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(stream);
message.Content.Headers.ContentLength = stream.Length;
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return message;
}
EDIT
Ok so this actually sends back the PDF when I write the proxy like this
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<FileStreamResult> OpenPDF([FromQuery] string url)
{
var fileStream = new MemoryStream();
var _httpClient = _httpClientFactory.CreateClient();
var file = await _httpClient.GetStreamAsync(url).ConfigureAwait(false);
await file.CopyToAsync(fileStream);
fileStream.Position = 0;
return File(fileStream, "application/pdf", "filename.pdf");
}
The problem is I want to update the content-disposition to inline so I can force this to open in the browser instead of downloading.So I decided to take the filestream and injecting that in the httpResponseMessage.content instead but that still didn't work. It would continue to send me a json file
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<HttpResponseMessage> OpenPDF([FromQuery] string url)
{
var fileStream = new MemoryStream();
var _httpClient = _httpClientFactory.CreateClient();
var file = await _httpClient.GetStreamAsync(url).ConfigureAwait(false);
await file.CopyToAsync(fileStream);
fileStream.Position = 0;
HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(fileStream);
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return message;
}
To be honest, I thought defining the content-type should suffice but guess not
This is pretty straight forward for .NET 6... suspect it should be roughly the same for .NET 4x... This uses the NuGet package Azure.Storage.Blobs
https://github.com/Azure/azure-sdk-for-net/blob/Azure.Storage.Blobs_12.13.1/sdk/storage/Azure.Storage.Blobs/README.md
[HttpGet("stream")]
public async Task GetBlobAsync()
{
var url = new Uri("https://path.to.blob.content/xxx");
var blobClient = new BlobClient(url);
Response.Headers.Add("Content-Type", "application/pdf");
Response.Headers.Add("Content-Disposition", #"attachment;filename=""intended file name.pdf""");
await blobClient.DownloadToAsync(Response.Body);
}
for .NET 4x.
try to add:
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("inline")
{
FileName = "filename.pdf"
};

How can I manipulate the Body of a Response with an ASP.NET Core Middleware?

I have the following Invoke-method in a middleware I'm playing with:
public async Task Invoke(HttpContext context)
{
var sw = new Stopwatch();
sw.Start();
context.Response.Headers.Add("X-SW-Start", "GO");
var stream = context.Response.Body;
var buffer = new MemoryStream();
context.Response.Body = buffer;
context.Response.Headers.Add("X-CanSeek", context.Response.Body.CanSeek.ToString());
context.Response.Headers.Add("X-CanRead", context.Response.Body.CanRead.ToString());
context.Response.Headers.Add("X-CanTimeout", context.Response.Body.CanTimeout.ToString());
context.Response.Headers.Add("X-CanWrite", context.Response.Body.CanWrite.ToString());
await next.Invoke(context);
context.Response.Headers.Add("X-SW-Stoppp", $"FINISH {sw.Elapsed.TotalSeconds}");
buffer.Seek(0, SeekOrigin.Begin);
var bufferString = new StreamReader(buffer).ReadToEnd();
bufferString = bufferString.Replace("[[[", "***");
var newBuffer = GenerateStreamFromString(bufferString);
await newBuffer.CopyToAsync(stream);
await stream.FlushAsync();
//context.Response.Body.Seek(0, SeekOrigin.Begin);
//var swr = new StreamWriter(buffer);
//swr.WriteLine("<!-- bob -->");
//await swr.FlushAsync();
}
Ignore all the response headers, writeline-debugging ftw...
This actually works, once, then all subsequent requests are empty except for the headers. All I really want to do is go to the top of the response-pipeline, and then run some global string-replace on the entire document body before it's sent to the browser. If anyone has any better way to do this then by all means...

HttpClient Post photo WITH message to Facebook Graph

Issue
I am trying to upload a photo to the Facebook API WITH a message.
Code Snippet - Upload
requestUri = "https://graph.facebook.com/v2.0/me/photos?access_token=MyAccessToken"
var streamContent = new StreamContent(fileStream);
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "\"files\"",
FileName = "\"image.jpg\""
};
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
var messageContent = new StringContent("message=HelloWorld");
var resultJson = webRequestClient.Post(requestUri, new MultipartFormDataContent()
{
messageContent,
streamContent,
});
Code - webRequestClient
public string Post(string uri, HttpContent postData)
{
return PostAsync(uri, postData).Result;
}
public async Task<string> PostAsync(string uri, HttpContent httpContent)
{
string resultStream;
using (var httpClient = new HttpClient())
{
var response = await httpClient.PostAsync(uri, httpContent);
response.EnsureSuccessStatusCode();
resultStream = await response.Content.ReadAsStringAsync();
}
return resultStream;
}
Notes
If I remove the "messageContent" : It uploads he picture
If I use MultipartContent : It uploads the picture but ignores my "message"
Don't bother for now why I don't do use the async functionality
When it fails I get "bad" requests
When I append the "message=helloworld" in the requestUri, it works, but that isn't the most flexible solution for in my architecture to deal with this issue.
Check this it will solve you problem, either you have to send the image via stream then you don't need to tell that the type is "image/jpeg" explicitly.
protected async void TakePictureAndUpload()
{
var ui = new CameraCaptureUI();
var file = await ui.CaptureFileAsync(CameraCaptureUIMode.Photo);
if (file != null)
{
byte[] myPicArray = await GetPhotoBytesAsync(file);
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://yourdomainname.com");
MultipartFormDataContent form = new MultipartFormDataContent();
HttpContent content = new ByteArrayContent(myPicArray);
form.Add(content, "media", "filename.jpg");
content = new StringContent("my-username");
form.Add(content, "username");
HttpResponseMessage response = await httpClient.PostAsync("directory/my-site.php", form);
}
}
public async Task<byte[]> GetPhotoBytesAsync(StorageFile file)
{
IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read);
var reader = new Windows.Storage.Streams.DataReader(fileStream.GetInputStreamAt(0));
await reader.LoadAsync((uint)fileStream.Size);
byte[] pixels = new byte[fileStream.Size];
reader.ReadBytes(pixels);
return pixels;
}

Windows store get download progress

After struggling for a long time i finally got a code for downloading a file with using authentication and sending a header with a range in a windows store application , now the next level is to get a progress bar of the download , so any idea of the classes and methods that should be used for getting the download operation?
This is my code:
var httpClientHandler = new HttpClientHandler();
httpClientHandler.Credentials = new System.Net.NetworkCredential("", "");
var client = new HttpClient(httpClientHandler);
System.Net.Http.HttpRequestMessage request = new System.Net.Http.HttpRequestMessage(HttpMethod.Post, new Uri(""));
request.Headers.Range = new RangeHeaderValue(0,null);
HttpResponseMessage response = await client.SendAsync(request);
Stream stream = await response.Content.ReadAsStreamAsync();
StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName);
await Windows.Storage.FileIO.WriteBytesAsync(file, ReadStream(stream));
This task will be easier if you switch from System.Net.Http.HttpClient to Windows.Web.Http.HttpClient.
using Windows.Web.Http;
using Windows.Web.Http.Filters;
private async void Foo(StorageFolder folder, string fileName)
{
Uri uri = new Uri("http://localhost");
var filter = new HttpBaseProtocolFilter();
filter.ServerCredential =
new Windows.Security.Credentials.PasswordCredential(uri.ToString(), "foo", "bar");
var client = new HttpClient(filter);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Headers.Add("Range", "bytes=0-");
// Hook up progress handler.
Progress<HttpProgress> progressCallback = new Progress<HttpProgress>(OnSendRequestProgress);
var tokenSource = new CancellationTokenSource();
HttpResponseMessage response = await client.SendRequestAsync(request).AsTask(tokenSource.Token, progressCallback);
IInputStream inputStream = await response.Content.ReadAsInputStreamAsync();
StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName);
// Copy from stream to stream.
IOutputStream outputStream = await file.OpenAsync(FileAccessMode.ReadWrite);
await RandomAccessStream.CopyAndCloseAsync(inputStream, outputStream);
}
private void OnSendRequestProgress(HttpProgress obj)
{
Debug.WriteLine(obj);
}
Otherwise, take a look at this other answer.

How to change the encoding of the HttpClient response

I'm trying to learn about Async programming using VS2012 and its Async Await keyword. That is why i wrote this piece of code:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
string get = await GetResultsAsync("http://saskir.medinet.se");
resultsTextBox.Text = get;
}
private async Task<string> GetResultsAsync(string uri)
{
HttpClient client = new HttpClient();
return await client.GetStringAsync(uri);
}
The problem is that when i try to debug the application, it gives me an error with this message:
The character set provided in ContentType is invalid. Cannot read content as string using an invalid character set.
I guess this is because the website have some Swedish char, but i can't find how to change the encoding of the response. Anyone can guide me plz?
You may have to check the encoding options and get the correct one. Otherwise, this code should get you going with the response.
private async Task<string> GetResultsAsync(string uri)
{
var client = new HttpClient();
var response = await client.GetByteArrayAsync(uri);
var responseString = Encoding.Unicode.GetString(response, 0, response.Length - 1);
return responseString;
}
In case you want a more generic method, following works in my UWP case in case someone has one with Unicode, would be great add the if:
var response = await httpclient.GetAsync(urisource);
if (checkencoding)
{
var contenttype = response.Content.Headers.First(h => h.Key.Equals("Content-Type"));
var rawencoding = contenttype.Value.First();
if (rawencoding.Contains("utf8") || rawencoding.Contains("UTF-8"))
{
var bytes = await response.Content.ReadAsByteArrayAsync();
return Encoding.UTF8.GetString(bytes);
}
}
WinRT 8.1 C#
using Windows.Storage.Streams;
using System.Text;
using Windows.Web.Http;
// in some async function
Uri uri = new Uri("http://something" + query);
HttpClient httpClient = new HttpClient();
IBuffer buffer = await httpClient.GetBufferAsync(uri);
string response = Encoding.UTF8.GetString(buffer.ToArray(), 0, (int)(buffer.Length- 1));
// parse here
httpClient.Dispose();

Categories

Resources