How to get response stream after uploading a file - c#

I am working on a metro app. I used background uploader to upload file but my question is how I get response value after uploading it. I coded like this:
BackgroundUploader uploader = new BackgroundUploader();
uploader.SetRequestHeader("Content-Disposition", "form-data");
uploader.SetRequestHeader("name", "userfile");
uploader.SetRequestHeader("filename", App.ViewModel.DeviceId + ".png");
uploader.SetRequestHeader("Content-Type", "multipart/form-data");
UploadOperation upload = uploader.CreateUpload(uri, file);
await upload.StartAsync();

I came up with the following after noticing there were BytesReceived in my upload progress object.
async private Task<string> GetUploadResponseBody(UploadOperation operation)
{
string responseBody = string.Empty;
using (var response = operation.GetResultStreamAt(0))
{
uint size = (uint)operation.Progress.BytesReceived;
IBuffer buffer = new Windows.Storage.Streams.Buffer(size);
var f = await response.ReadAsync(buffer, size, InputStreamOptions.None);
using (var dr = DataReader.FromBuffer(f))
{
responseBody = dr.ReadString(dr.UnconsumedBufferLength);
}
}
return responseBody;
}

upload.StartAsync().Completed = UploadCompletedHandler;
...
void UploadCompletedHandler(IAsyncOperationWithProgress<TResult, TProgress> asyncInfo,
AsyncStatus asyncStatus)
{
// get a response body from an asyncInfo using the asyncInfo.GetResults() method
}
Follow this resources:
UploadOperation.StartAsync | startAsync Method (Windows)
IAsyncOperationWithProgress Interface (Windows)
AsyncOperationWithProgressCompletedHandler Delegate (Windows)

I've been looking for the same thing for the last few days and no luck. Finally discovered that you can not do this. You can get the "headers" of the response but there is no way of getting the "body" of the response from a BackgroundTransfer getResponseInformation() method.
Currently it's a limitation of the windows API. Hope they'll add it soon.
http://msdn.microsoft.com/en-us/library/windows/apps/windows.networking.backgroundtransfer.responseinformation.aspx#properties
The workaround is you can add your custom header in the response. For this you need to modify your server side script. But if you don't have any control on your server side script then use a proxy script that will do the communication between your app and remote server. For my case I created a proxy script in php that communicates with the remote server and after getting the response I'm adding it into a custom header key.
Then in the app in your complete method use this:
function complete(e){
var mykey = e.getResponseInformation().headers.lookup("mykey");
}
Hope that'll help.

Related

Can you download a file from an HttpContent stream from within a Windows Forms Application?

I recently developed a .NET Web App that downloaded zip files from a certain, set location on our network. I did this by retrieving the content stream and then passing it back to the View by returning the File().
Code from the .NET Web App who's behavior I want to emulate:
public async Task<ActionResult> Download()
{
try
{
HttpContent content = plan.response.Content;
var contentStream = await content.ReadAsStreamAsync(); // get the actual content stream
if (plan.contentType.StartsWith("image") || plan.contentType.Contains("pdf"))
return File(contentStream, plan.contentType);
return File(contentStream, plan.contentType, plan.PlanFileName);
}
catch (Exception e)
{
return Json(new { success = false });
}
}
plan.response is constructed in a separate method then stored as a Session variable so that it is specific to the user then accessed here for download.
I am now working on a Windows Forms Application that needs to be able to access and download these files from the same location. I am able to retrieve the response content, but I do not know how to proceed in order to download the zip within a Windows Forms Application.
Is there a way, from receiving the content stream, that I can download this file using a similar method within a Windows Form App? It would be convenient as accessing the files initially requires logging in and authenticating the user and thus can not be accessed normally with just a filepath.
Well, depending on what you're trying to accomplish, here's a pretty simplistic example of download a file from a URL and saving it locally:
string href = "https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-zip-file.zip";
WebRequest request = WebRequest.Create(href);
using (WebResponse response = request.GetResponse())
{
using (Stream dataStream = response.GetResponseStream())
{
Uri uri = new Uri(href);
string fileName = Path.GetTempPath() + Path.GetFileName(uri.LocalPath);
using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate))
{
dataStream.CopyTo(fs);
}
}
}

Wikipedia user agent problem when downloading images

I am trying to download about 250 images from wikipedia with a c# .net console application.
After downloading 3 I get this error.
System.Net.WebException: 'The remote server returned an error: (403) Forbidden. Please comply with the User-Agent policy: https://meta.wikimedia.org/wiki/User-Agent_policy. '
I have read their User-Agent_policy page and added a user agent that complies with what they say. (to the best of my ability, I'm not a web-dev)
They say, make it descriptive, include the word bot if its a bot, include contact details in parentheses, all of which I have done.
I am also waiting 5 seconds in between each image.. I just really really dont wanna download them by hand in my browser.
static void DownloadImages()
{
var files = Directory.GetFiles(#"C:\projects\CarnivoraData", "*", SearchOption.AllDirectories);
var client = new WebClient();
client.Headers.Add("User-Agent", "bot by <My Name> (<My email address>) I am downloading an image of each carnivoran once (less than 300 images) for educational purposes");
foreach (var path in files)
{
//Console.WriteLine(path);
//Console.WriteLine(File.ReadAllText(path));
AnimalData data = JsonSerializer.Deserialize<AnimalData>(File.ReadAllText(path));
client.DownloadFile("https:" + data.Imageurl,#"C:\projects\CarnivoraImages\"+ data.Name +Path.GetExtension(data.Imageurl));
System.Threading.Thread.Sleep(5000);
}
}
Any suggestions?
Ok I got this to work. I think they key was using httpclient to download the files instead of webclient, and using DefaultRequestHeaders.UserAgent.ParseAdd
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("<My Name>/1.0 (<My Email>) bot");
I didnt even bother waiting between images, downloaded them all in about a minute.
Also as a bonus, heres how to download a file using httpclient (its a lot messier than webclient!)
static async Task GetFile(HttpClient httpClient,string filepath, string url)
{
using (var stream = await httpClient.GetStreamAsync(new Uri(url)))
{
using (var fileStream = new FileStream(filepath, FileMode.CreateNew))
{
await stream.CopyToAsync(fileStream);
}
}
}

Incomplete Azure download

I'm having a problem with inclomplete blobs being downloaded from Azure storage. The files that are stored are an images. Almost every file that's downloaded ends up missing several lines on the bottom. I've checked the blobs and they were uploaded correctly.
I'm using the following code for downloading a blob from the Azure service:
private async Task Download(CloudBlobClient client)
{
try
{
_media = await _directory.CreateFileAsync(ResourceName, CreationCollisionOption.FailIfExists);
}
catch (Exception)
{
return;
}
using (var stream = await _media.OpenAsync(FileAccessMode.ReadWrite))
{
var blob = await GetBlob(client);
await blob.DownloadToStreamAsync(stream);
_category.NotifyAzureProgress();
await stream.FlushAsync();
}
}
The method GetBlob() looks like this:
private async Task<CloudBlockBlob> GetBlob(CloudBlobClient client)
{
CloudBlobContainer container = client.GetContainerReference(ContainerName);
await container.CreateIfNotExistsAsync();
var blob = container.GetBlockBlobReference(ResourceName);
return blob;
}
Upload code:
private async Task UploadAsync(CloudBlobClient client)
{
_media = await _directory.GetFileAsync(ResourceName);
using (var stream = await _media.OpenAsync(FileAccessMode.Read))
{
var blob = await GetBlob(client);
await blob.UploadFromStreamAsync(stream);
_category.NotifyAzureProgress();
}
}
Thanks for any help!
Edit: I've realized I've missed out one detail - the downloaded image has correct dimensions, but several lines from the bottom are black - it doesn't has the same pixels as the source image. I've checked the MD5 hashes and while they match, when I download the image through an external app, they don't match when I download them with the code above.
Edit2: after inspecting the properties of CloudBlob and the output stream, I've noticed, that even though the blob gives correct length after download, the stream usually says something a little lower. I've tried downloading throught range, but to no avail
Ok, so I've managed to download the images afterall, by partially using the WinRT Azure library combined with a standard .NET HttpClient.
I used the Azure Lib establish the initial connection and then to get only the Blob reference, because the BlockBlobReference has a method to create Shared Access Signature (and I really didn't want to try to construct it myself). Then I created the HttpClient, made a download URL using the SAS and issued a GET request to the URL, which finally worked and downloaded all the images intact.
I think there might be some weird bug in the official library, since using my download method instead of theirs solved everything.
Code sample:
internal async Task Download(CloudBlobClient client)
{
try
{
_media = await _directory.CreateFileAsync(ResourceName, CreationCollisionOption.FailIfExists);
}
catch (Exception)
{
return;
}
try
{
var blob = await GetBlob(client);
HttpClient httpClient = new HttpClient();
var date = DateTime.UtcNow;
var policy = new SharedAccessBlobPolicy();
policy.Permissions = SharedAccessBlobPermissions.Read;
policy.SharedAccessStartTime = new DateTimeOffset(date);
policy.SharedAccessExpiryTime = new DateTimeOffset(date.AddDays(1));
var signature = blob.GetSharedAccessSignature(policy);
var uriString = string.Format("{0}{1}", blob.Uri.ToString(), signature);
var data = await httpClient.GetByteArrayAsync(uriString);
var buf = new Windows.Storage.Streams.Buffer((uint)data.Length);
await FileIO.WriteBytesAsync(_media, data);
_category.NotifyAzureProgress();
}
catch (Exception e)
{
_media.DeleteAsync();
throw e;
}
}

Unable to share Bitmap Image and Html Content together?

I am creating an application where I am sharing a InkManager stroke by first saving the inkmanager stroke to local setting and then reading it as stream and sharing.
Now the HTML content is getting shared but there is no image attached to email. If I comment the code to share html the image is getting attached while sharing through email app. But both are not getting shared at once. How can I achieve this???
Code:
DataPackage requestData = request.Data;
requestData.Properties.Title = this.PageViewModel.JobInformationDetail.JobNumber;
requestData.Properties.Description = this.PageViewModel.JobInformationDetail.CustomerSignatureName;
//requestData.SetText("Sample Text");
RandomAccessStreamReference signatureStream = await GetInkManagerStream();
if (signatureStream != null)
{
requestData.SetBitmap(signatureStream);
}
requestData.SetHtmlFormat(Windows.ApplicationModel.DataTransfer.HtmlFormatHelper.CreateHtmlFormat(this.GetMailDescription()));
Convert InkManager to stream
private async Task<RandomAccessStreamReference> GetInkManagerStream()
{
IRandomAccessStream signatureStream = null;
StorageFile myMerge = await ApplicationData.Current.LocalFolder.CreateFileAsync("sign.png", CreationCollisionOption.OpenIfExists);
IOutputStream signature = await myMerge.OpenAsync(FileAccessMode.ReadWrite);
if (signature != null)
{
await _inkManager.SaveAsync(signature);
signature.Dispose();
}
signatureStream = await myMerge.OpenAsync(FileAccessMode.ReadWrite);
RandomAccessStreamReference streamRef = RandomAccessStreamReference.CreateFromStream(signatureStream);
signatureStream.Dispose();
return streamRef;
}
The problem is caused by awaiting an async method inside your DataRequested event handler. If you're doing this, you need to use DataRequestReferral:
private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
// the rest of your method, including the awaiting call
deferral.Complete();
}
By doing this you're telling the caller that it needs to wait for an async call to complete before continuing. At the end you signal it to continue by calling deferral.Complete(). Without it the caller continues before you actually set the bitmap, thus the error.
EDIT:
Windows 8 Mail app is not a share target for images shared like this, though. For the Mail app to be available you need to share the image as a storage item:
var file = await ApplicationData.Current.LocalFolder.GetFileAsync(#"sign.png");
var imageItems = new List<IStorageItem>();
imageItems.Add(file);
requestData.SetStorageItems(imageItems);
Now Mail will show up as one of available targets. When you're sharing images, it's best to use both SetBitmap() and SetStorageItems() to get more share targets available since not all apps support both data types.
EDIT 2:
Also the Mail app only picks up one type of shared content, either HTML or StorageItems (if no HTML is provided) as you've already noticed. The only workaround I know of, is to embed the images into the HTML content:
var resourceName = "logo.png";
var html = String.Format("<p>HTML content</p><img src='{0}'/>", resourceName);
requestData.ResourceMap[resourceName] = RandomAccessStreamReference.CreateFromUri(
new Uri("ms-appdata:///local/sign.png"));
requestData.SetHtmlFormat(HtmlFormatHelper.CreateHtmlFormat(html));
You can read more about this approach in a blog post by Mike Taulty.

My post request to https://qrng.physik.hu-berlin.de/ failed, why?

the page at https://qrng.physik.hu-berlin.de/ provides a high bit rate quantum number generator web service and I'm trying to access that service.
However I could not manage to do so. This is my current code:
using System;
using System.Collections.Generic;
using System.Linq;
using S=System.Text;
using System.Security.Cryptography;
using System.IO;
namespace CS_Console_App
{
class Program
{
static void Main()
{
System.Net.ServicePointManager.Expect100Continue = false;
var username = "testuser";
var password = "testpass";
System.Diagnostics.Debug.WriteLine(Post("https://qrng.physik.hu-berlin.de/", "username="+username+"&password="+password));
Get("http://qrng.physik.hu-berlin.de/download/sampledata-1MB.bin");
}
public static void Get(string url)
{
var my_request = System.Net.WebRequest.Create(url);
my_request.Credentials = System.Net.CredentialCache.DefaultCredentials;
var my_response = my_request.GetResponse();
var my_response_stream = my_response.GetResponseStream();
var stream_reader = new System.IO.StreamReader(my_response_stream);
var content = stream_reader.ReadToEnd();
System.Diagnostics.Debug.WriteLine(content);
stream_reader.Close();
my_response_stream.Close();
}
public static string Post(string url, string data)
{
string vystup = null;
try
{
//Our postvars
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(data);
//Initialisation, we use localhost, change if appliable
System.Net.HttpWebRequest WebReq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
//Our method is post, otherwise the buffer (postvars) would be useless
WebReq.Method = "POST";
//We use form contentType, for the postvars.
WebReq.ContentType = "application/x-www-form-urlencoded";
//The length of the buffer (postvars) is used as contentlength.
WebReq.ContentLength = buffer.Length;
//We open a stream for writing the postvars
Stream PostData = WebReq.GetRequestStream();
//Now we write, and afterwards, we close. Closing is always important!
PostData.Write(buffer, 0, buffer.Length);
PostData.Close();
//Get the response handle, we have no true response yet!
System.Net.HttpWebResponse WebResp = (System.Net.HttpWebResponse)WebReq.GetResponse();
//Let's show some information about the response
Console.WriteLine(WebResp.StatusCode);
Console.WriteLine(WebResp.Server);
//Now, we read the response (the string), and output it.
Stream Answer = WebResp.GetResponseStream();
StreamReader _Answer = new StreamReader(Answer);
vystup = _Answer.ReadToEnd();
//Congratulations, you just requested your first POST page, you
//can now start logging into most login forms, with your application
//Or other examples.
}
catch (Exception ex)
{
throw ex;
}
return vystup.Trim() + "\n";
}
}
}
I'm having 403 forbidden error when I try to do a get request on http://qrng.physik.hu-berlin.de/download/sampledata-1MB.bin.
After debugging abit, I've realised that even though I've supplied a valid username and password, the response html that was sent after my POST request indicate that I was actually not logon to the system after my POST request.
Does anyone know why is this the case, and how may I work around it to call the service?
Bump. can anyone get this to work or is the site just a scam?
The site is surely not a scam. I developed the generator and I put my scientific reputation in it. The problem is that you are trying to use the service in a way that was not intended. The sample files were really only meant to be downloaded manually for basic test purposes. Automated access to fetch data into an application was meant to be implemented through the DLLs we provide.
On the other hand, I do not know of any explicit intent to prevent your implementation to work. I suppose if a web browser can log in and fetch data, some program should be able to do the same. Maybe only the login request is just a little more complicated. No idea. The server software was developed by someone else and I cannot bother him with this right now.
Mick
Actually, the generator can now also be purchased. See here:
http://www.picoquant.com/products/pqrng150/pqrng150.htm
Have you tried to change this
my_request.Credentials = System.Net.CredentialCache.DefaultCredentials
to
my_request.Credentials = new NetworkCredential(UserName,Password);
as described on MSDN page?

Categories