Azure function - request image from 3d party then send image to requestor without saving to local directory - image

I've found lots of questions about downloading images and as my code shows that is what I ended up doing. However that is not the behavior I want. I just want it to return the image directly.
using System.Net;
using Microsoft.Extensions.Logging;
using System.IO;
public static async Task<HttpResponseMessage> Run(HttpRequest req, ILogger log, string data)
{
log.LogInformation("start function...");
string qrData = $"{data}";//req.Query["id"];
string QrGeneratorUrl = "https://api.qrserver.com/v1/create-qr-code/?size=100x100&data="+ qrData;
log.LogInformation("QrUrl= " + QrGeneratorUrl);
var filename = "temp.png";
var filePath = Path.Combine(#"d:\home\site\wwwroot\QrGeneratorTest\"+filename);
WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(QrGeneratorUrl, filePath);
var response = new HttpResponseMessage(HttpStatusCode.OK);
var fileStream = new FileStream(filePath, FileMode.Open);
response.Content = new StreamContent(fileStream);
return response;
}
I've tried converting the image to a byte stream and adding the stream to the response content, I've tried putting the image data directly as string content... nothing seems to work - it only transmits the image if it is a local file and I add it to the response via fileStream. Does someone know how I can get it to just put the response I get into the response I am returning? Or explain why it can't be done? This is functionality that exists in a web app that we are trying the move into a function and the web app is able to pass the content along without saving it. Using a byte stream. But I can't seem to replicate that in the function.
There are 2 reasons we are not calling qr server directly
1) it's a 3d party site so it could go down and we need to be able to swap it out for a new provider from one location.
2) we need to build the url so it has not parameters (?p=1&q=2&r=3...) as this is going into an email and having a bunch of parameters often tags the email as junk. With Azure (as with our web app) we can build the url like this: /getImage/1/2/3 which is less likely to be tagged as spam
any insight would be appreciated!!
//*******************//
ANSWER
here is my final code. I think the issue was Stream vs MemoryStream... In any case here is the full code:
#r "Newtonsoft.Json"
using System.Net;
using System;
using System.Web;
using System.Threading.Tasks;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
public static async Task<HttpResponseMessage> Run(HttpRequest req, ILogger log, string data)
{
log.LogInformation("start function...");
string qrData = $"{data}";
//string qrData = DateTime.Now.Ticks.ToString();
string QrGeneratorUrl = "https://api.qrserver.com/v1/create-qr-code/?size=100x100&qzone=2&data="+ qrData;
//get the QR image from 3d party api
var httpWebRequest = WebRequest.Create(QrGeneratorUrl);
var httpResponse = await httpWebRequest.GetResponseAsync();
//put 3d party response into function response
Stream ms = httpResponse.GetResponseStream(); //new MemoryStream(bytes);
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(ms);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return result;
}

Suppose you want to download the image to stream and just return it(If use browser send the request, show the image in the browser, If I get it wrong please let me know). If this is your purpose you could refer to my below code, I download the image from blob and return it to FileContentResult.
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System.DrawingCore;
namespace FunctionApp72
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
CloudStorageAccount blobAccount = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("AzureWebJobsStorage"));
CloudBlobClient blobClient = blobAccount.CreateCloudBlobClient();
CloudBlobContainer blobContainer = blobClient.GetContainerReference("test");
CloudBlockBlob cloudBlockBlob = blobContainer.GetBlockBlobReference("test.jpg");
MemoryStream streamIn = new MemoryStream();
await cloudBlockBlob.DownloadToStreamAsync(streamIn);
Image originalImage = Bitmap.FromStream(streamIn);
return new FileContentResult(ImageToByteArray(originalImage), "image/jpeg");
}
private static byte[] ImageToByteArray(Image image)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(image, typeof(byte[]));
}
}
}
And I deploy it to azure It still could return the image.

Related

Client-Side error when uploading image on server ASP.NET Core

I am struggling with uploading an image from thew client-side to a folder on the server-side in .Net Core.I used Postman to check if the method on the server-side is working and it does without any problem,but when I try to upload an image from the client-side,I get an error on the server-side of type NullReferenceException:Object reference not set to an instance of an object.This is the Post method on the server-side:
[HttpPost]
public async Task Post(IFormFile file)
{
if (string.IsNullOrWhiteSpace(_environment.WebRootPath))
{
_environment.WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
}
var uploads = Path.Combine(_environment.WebRootPath, "uploads");
//var fileName = file.FileName.Split('\\').LastOrDefault().Split('/').LastOrDefault();
if (!Directory.Exists(uploads)) Directory.CreateDirectory(uploads);
if (file.Length > 0)
{
using (var fileStream = new FileStream(Path.Combine(uploads, file.FileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
}
}
Apparently the method is thrown where I check if the length of the file is bigger than 0.On the client-side I get error "500 internal server error" and I tried to check using the debugger where exactly the error is thrown but i can't find anything that could resemble an error of some sort.This is the API method for the client-side:
public async Task UploadPictureAsync(MediaFile image)
{
User user = new User();
string pictureUrl = "http://10.0.2.2:5000/api/UploadPicture";
HttpContent fileStreamContent = new StreamContent(image.GetStream());
// user.Picture=GetImageStreamAsBytes(image.GetStream());
fileStreamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {FileName=Guid.NewGuid() + ".Png",Name="image"};
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
using (var client = new HttpClient(clientHandler))
{
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileStreamContent);
var response = await client.PostAsync(pictureUrl, formData);
if(response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
}
}
}
}
The image is declared in the Model as byte array:
public byte[] Picture { get; set; }
Does someone understand why my POST method has this behavior since the server-side works perfectly but fails when I try to upload an image from the client-side?What I find weird though is that when i read the error and I look at the Content-Type it is "text/plain" instead of "form-data" and I have tried to set it at the MutipartFormDataContent like this:
formData.Headers.ContentType.MediaType = "multipart/form-data";
I also tried to set the MediaTypeHeaderValue on the client like this:
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
I still get the wrong content type.
I have also tried a different approach with Stream instead of MediaFile but without any luck as it did not even hit the break point in debugger mode for the response.Any help would be appreciated! :)
I have managed to find the answer finalllyyyyy!!!The problem was on the client-side as I suspected and guess what,it was all about the correct name.It turns out that since on the server side I have IFormFile file I had to change the client side to take the parameter name "file" instead of image as well so that it could work.Thank you #Jason for the suggestions as I didn't understand the error from the first place and did some debugging on the server-side to help me figure it out.

How to connect Stanford Core Server from dotnet

I am trying to use Stanford NLP for.NET. I am very new to this.
How can I connect Stanford core NLP server from c# program
My NLP server runs on localhost:9000
You can connect via the .NET HTTPClient or other equivalent .NET call to a web endpoint. You need to set up your NLP endpoint, properties for the NLP server, and the text content you want it to parse. There is additional information on the Stanford NLP Server page, as well as information on what properties can be set depending on what NLP pipeline you want to run.
The following code is from a .NET Core console application using the following call to return Named Entity Recognition, Dependency Parser and OpenIE results.
FYI I've had some occasions where my NLP endpoint didn't work when waking a laptop from sleep mode (Docker for Windows pre 17.12 on Win10). Resetting Docker did the trick for me... if you can't browse to your http://localhost:9000 website, then the endpoint definitely won't work either!
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net.Http;
// String to process
string s = "This is the sentence to provide to NLP."
// Set up the endpoint
string nlpBaseAddress = "http://localhost:9000"
// Create the query string params for NLPCore
string jsonOptions = "{\"annotators\": \"ner, depparse, openie\", \"outputformat\": \"json\"}";
Dictionary qstringProperties = new Dictionary();
qstringProperties.Add("properties", jsonOptions);
string qString = ToQueryString(qstringProperties);
// Add the query string to the base address
string urlPlusQuery = nlpBaseAddress + qString;
// Create the content to submit
var content = new StringContent(s);
content.Headers.Clear();
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
// Submit for processing
var client = new HttpClient();
HttpResponseMessage response;
Task tResponse = client.PostAsync(urlPlusQuery, content);
tResponse.Wait();
response = tResponse.Result;
// Check the response
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
// Do something better than throwing an app exception here!
throw new ApplicationException("Subject-Object tuple extraction returned an unexpected response from the subject-object service");
}
Task rString = response.Content.ReadAsStringAsync();
rString.Wait();
string jsonResult = rString.Result;
Utility function used within this call to generate a QueryString:
private string ToQueryString(Dictionary nvc)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder("?");
bool first = true;
foreach (KeyValuePair key in nvc)
{
// CHeck if this is the first value
if (!first)
{
sb.Append("&");
}
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key.Key), Uri.EscapeDataString(key.Value));
first = false;
}
return sb.ToString();
}

What is the alternative for HttpContext.Response.OutputStream to use in WebAPI's HttpResponseMessage

I'm writing a WebAPI for handling PDF documents. It was written in a ashx page earlier implementing IHttpHandler and getting the context using HttpContext. I'm now writing it using WebAPI. In WebAPI we have HttpResponseMessage. For HttpContext.Response.BinaryWrite we have new ByteArrayContent in HttpResponseMessage. But what is the alternative for HttpContext.Response.OutputStream in WebAPI? I need to have the alternative of OutputStram in WebAPI because im passing this OutputStream as a parameter to another dll.
Code in ashx:
SomeReport.PdfReport rpt = new SomeReport.PdfReport(docID);
rpt.CreateReport(context.Response.OutputStream);
Actually you can use any stream for example MemoryStream but result should be wrapped into StreamContent.
public HttpResponseMessage Get()
{
var response = Request.CreateResponse();
var outputStream = new MemoryStream();
//write data to output stream
//or passing it to somewhere
outputStream.WriteByte(83);
outputStream.Position = 0;
response.Content = new StreamContent(outputStream);
return response;
}
If you need direct writing to output stream, please consider using PushStreamContent. Example

Download embedded images from google document

I'm trying to download embedded images in a google document using their Drive API and WebClient. A few of the images works just fine, and that is pure images. The others responds with a redirect to the login page instead of the file, so i suppose it has something to do with the credentials (I'm not setting any credetials to my WebClient right now). The images that fail looks like they are called drawings instead of images. Can that be the issue here?
The links that breaks looks like this:
https://docs.google.com/a/irissystem.se/drawings/image?id=HERE_IS_AN_UNIQUE_ID&rev=1&h=81&w=28&ac=1
Is there a way to download images like this using the HttpClient of DriveService-class or a way to apply my credentials from my DriveService to my WebClient instance?
The code below is used to parse the document and download the images.
foreach (HtmlNode img in doc.DocumentNode.SelectNodes("//img")) {
HtmlAttribute src = img.Attributes["src"];
using (WebClient webClient = new WebClient()) {
byte[] data = webClient.DownloadData(src.Value);
using (MemoryStream imagestream = new MemoryStream(data)) {
byte[] imagebinary = imagestream.ToArray();
Images.Add(src.Value, imagebinary);
}
}
}
UPDATE
Thanks to the comment below, i started thinking about the HttpClient and download stream and it turned out to be a good solution. The code below uses my Google DataService (authenticated and done) to download the embedded file as a stream. This works for both drawings and images, so it is a all round solution.
public byte[] GetFileByUrl(string Url, string ExportType = "text/plain") {
var stream = Service.HttpClient.GetStreamAsync(Url);
var result = stream.Result;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
result.CopyTo(ms);
return ms.ToArray();
}
}
The code for auth looks like this
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(SERVICE_ACCOUNT_EMAIL)
{
User = "xxx#xxx.com",
Scopes = new[] { DriveService.Scope.Drive }
}.FromCertificate(certificate));
Service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "xxx",
});

Google apps Directory API (1.6 and above) DotNetOpenAuth not resolving

This code is changing fast and hard to get a handle on what works and what doesn't...
I was looking at this post: Have you used Google's Directory API?
Which is using the 1.4 library.
I installed the 1.6 API through nuget. However, the NativeApplicationClient and IAuthorizationState cannot be resolved. I was under the impression that I no longer needed the DotNetOpenAuth nuget package or the Google.Apis.Authentication package (which is where I believe they are resolved.
This is the complete and modified code I am playing with: (if you have a better example of creating users using the new API I'd like to see that!)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;
using Google;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth;
using Google.Apis.Download;
using Google.Apis.Logging;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Admin.Directory;
using Google.Apis.Admin.Directory.directory_v1.Data;
namespace GoogleAddUser
{
class Program
{
static void Main(string[] args)
{
// Display the header and initialize the sample.
//CommandLine.EnableExceptionHandling();
Console.WriteLine("Create users in a google apps domain!");
Console.WriteLine("by Jonas Bergstedt 2013");
// Get the user data and store in user object
Console.Write("Email: ");
string userId = Console.ReadLine();
Console.Write("Givenname: ");
string GivenName = Console.ReadLine();
Console.Write("Familyname: ");
string FamilyName = Console.ReadLine();
Console.Write("Password: ");
string Password = Console.ReadLine();
User newuserbody = new User();
UserName newusername = new UserName();
newuserbody.PrimaryEmail = userId;
newusername.GivenName = GivenName;
newusername.FamilyName = FamilyName;
newuserbody.Name = newusername;
newuserbody.Password = Password;
// Register the authenticator.
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = "<your clientId from Google APIs Console>",
ClientSecret = "<your clientsecret from Google APIs Console>",
};
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
// Create the service.
var service = new DirectoryService(new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = "Create User",
ApiKey = "<your API Key from Google APIs console> (not sure if needed)"
});
User results = service.Users.Insert(newuserbody).Execute();
Console.WriteLine("User :" + results.PrimaryEmail + " is created");
Console.WriteLine("Press any key to continue!");
Console.ReadKey();
}
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { DirectoryService.Scopes.AdminDirectoryUser.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.WriteLine();
Console.Write("Authorization Code: ");
string authCode = Console.ReadLine();
// Retrieve the access token by using the authorization code:
return arg.ProcessUserAuthorization(authCode, state);
}
}
}
From release 1.6.0-beta we presented a new Google.Apis.Auth NuGet package (Google.Apis.Authentication which uses DNOA is obsolete!). You already installed that package, because all the new APIs have a reference to it. Take a look in our OAuth2 wiki page for more details about how to use the new OAuth2 flows (it's not magic anymore, now the flows actually make sense!)
I recommend you subscribing to our announcement blog and optionally to my personal blog to get more information about the client library. In our announcement blog we described the reason the new OAuth2 pacakge.
Hope it is helpful.

Resources