Upload Binary file with HttpClient using octet stream - c#

I am trying to upload a file using an httpClient as an octet-stream
Following documentation is provided
HTTP Verb => POST
Content–Type => application/octet-stream
Accept => application/json
Authorization => Bearer {access token}
x-file-name => Test file.pdf
x-convert-document (optional) => true
x-source => the source (can be any string)
Request Body => file (binary Stream)
Side note : in my code below the Accept and Bearer headers and their values are already present in the Client I am fetching with the "await GetRestClientAsync()"
I expect a response back, but for now I am getting nothing back the application just stops, what am I doing wrong here?
Ps: Other Posts, request towards this api work fine with the same client I use here, so it's most likely that I am messing something up with the MultiPartFormDataContent
protected async Task<TResponse> ExecutePostAsBinaryCustomHeadersAsync<TResponse>(Uri apiUri, Stream fileStream, string fileName ,bool returnDefaultObjectForInternalServerError = false)
{
using (var formData = new MultipartFormDataContent())
{
formData.Add(new StringContent(fileName), "x-file-name");
formData.Add(new StringContent("true"), "x-convert-document");
formData.Add(new StringContent("Agion"), "x-source");
formData.Add(new StringContent("application/octet-stream"), "Content-Type");
using (var client = await GetRestClientAsync())
{
StreamContent content = new StreamContent(fileStream);
formData.Add(content, "file", fileName);
using (var response = await client.PostAsync(apiUri, formData))
{
return await DeserializeResponseAsync<TResponse>(response, returnDefaultObjectForInternalServerError);
}
}
}
}

Related

Upload file to SharePoint using HTTP Client is not working

I want to upload a static file to SharePoint using Graph API and HTTP Client.
We have a folder where the file is kept, now we have to read the file from the folder and upload it to SharePoint.
Stream stream = GetFile() //this will return file data as a Stream.
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", token);
MultipartFormDataContent form = new MultipartFormDataContent();
var streamContent = new StreamContent(stream);
var imageContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
form.Add(imageContent, "Test.pdf");
var response = httpClient.PostAsync(#"https://graph.microsoft.com/v1.0/sites/{siteId}/lists/{folderName}/drive/root:/childFolder/Test.pdf:/createUploadSession", form).Result;
We are getting BadRequest response for the above request.
According to the documentation no request body is required for createUploadSession endpoint but if you specify the body it must be json like this
Content-Type: application/json
{
"item": {
"#odata.type": "microsoft.graph.driveItemUploadableProperties",
"#microsoft.graph.conflictBehavior": "rename",
"name": "largefile.dat"
}
}
The response will contain uploadSession resource type.
To upload the file, or a portion of the file, you need to make a PUT request to the uploadUrl value received in the createUploadSession response. You can upload the entire file, or split the file into multiple byte ranges, as long as the maximum bytes in any given request is less than 60 MiB.
Resources:
driveItemUploadableProperties
uploadSession
Upload bytes to the upload session

How can I make a http client that sends Base64 encrypted octet stream via Multipart form data?

Context
In my company we have a API that's very tricky to handle. I managed to make a successful PUT Request using Postman and now I want to build this same http request in C# using a simple Console application.
Here is the postman request:
The 2nd key has to be named exactly like that. The entry Json I can use via file or directly as value.
Here are the headers:
Only important one is the Authorization Header.
The problem
I don't know how to actually create this complicated request in C# since I'm very new to this language and couldn't find a solution to my specific problem.
I tried with the normal httpclient from C# and RestSharp but wasn't able to make this request.
Here is what I have so far:
{
class Program
{
static readonly HttpClient client = new HttpClient();
static async Task Main(string[] args)
{
using var multipart = new MultipartFormDataContent();
var jsonBytes = JsonSerializer.SerializeToUtf8Bytes(new { Metadata = "abc" });
// Need to add my json file or the json direct here somewhere
// This is how the JSON looks like
/*
{
"values": {
"z1D_WorklogDetails": "very new workinfo 3",
"z1D_View_Access": "Internal",
"z1D Action": "MODIFY",
"z2AF_Act_Attachment_1": "UID Liste.xlsx"
}
}
*/
multipart.Add(new ByteArrayContent(jsonBytes), "entry");
using var fs = File.OpenRead(#"C:\myFile.txt");
multipart.Add(new StreamContent(fs), "attach-z2AF_Act_Attachment_1");
multipart.Headers.Add("Authorization", "//my token here");
using var resp = await client.PostAsync("**/entry/HPD:IncidentInterface/INC000001479529|INC000001479529", multipart);
resp.EnsureSuccessStatusCode();
}
}
}
So how can I make this complicated request like the on shown in Postman exactly the same in C#? The API Admins told me the attachment in attach-z2AF_Act_Attachment_1 has to come Base64 encrypted
For anyone that is interested what this call actually does:
It adds a new Worklog to an existing ticket in our ticket system (BMC Remedy) and also adds an attachment to this new worklog entry.
Thank you very much.
Please look at the code below, please test it at your environment.
The point is that you can set content types manually.
Another point is that you set Authorization header wrong.
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
string url = "https://localhost/";
string token = "token_here";
//Prepare json data
string json = JsonSerializer.Serialize(new { Metadata = "abc" });
StringContent jsonContent = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
StreamContent streamContent;
bool base64 = false;
//Prepare octet-stream data
if (base64)
{
//For base-64 encoded message
using FileStream inputFile = new FileStream(#"2.txt", FileMode.Open, FileAccess.Read, FileShare.None,
bufferSize: 1024 * 1024, useAsync: true);
using CryptoStream base64Stream = new CryptoStream(inputFile, new ToBase64Transform(), CryptoStreamMode.Read);
streamContent = new StreamContent(base64Stream);
streamContent.Headers.Add("Content-Type", MediaTypeNames.Application.Octet);
await SendRequest(jsonContent, streamContent, url, token);
}
else
{
//For plain message
using FileStream file = File.OpenRead("2.txt");
streamContent = new StreamContent(file);
streamContent.Headers.Add("Content-Type", MediaTypeNames.Application.Octet);
await SendRequest(jsonContent, streamContent, url, token);
}
async Task SendRequest(StringContent stringContent, StreamContent streamContent, string url, string token)
{
// Add json and octet-stream to multipart content
MultipartFormDataContent multipartContent = new MultipartFormDataContent();
multipartContent.Add(stringContent, "entry");
multipartContent.Add(streamContent, "attach-z2AF_Act_Attachment_1");
//Create request
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, url);
//Here is the right setting of auth header value, sheme is on the left side
request.Headers.Authorization = new AuthenticationHeaderValue("AR-JWT", token);
request.Content = multipartContent;
//Last step - sending request
HttpClient http = new HttpClient();
HttpResponseMessage resp = await http.SendAsync(request);
resp.EnsureSuccessStatusCode();
}
With my approach i got this request:
Headers:
{
"content-length": "6538",
"authorization": "AR-JWT token_here",
"content-type": "multipart/form-data; boundary=\"02600173-b9af-49f4-8591-e7edf2c0b397\""
}
Body:
--02600173-b9af-49f4-8591-e7edf2c0b397
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name=entry
{"Metadata":"abc"}
--02600173-b9af-49f4-8591-e7edf2c0b397
Content-Type: application/octet-stream
Content-Disposition: form-data; name=attach-z2AF_Act_Attachment_1
OCTET DATA HERE
--02600173-b9af-49f4-8591-e7edf2c0b397--
Is it correct?
Update: I've made a base-64 encoded version of attachment.
Simply set base64 to true.
Request with base64 approach:
Headers:
{
"content-length": "354",
"authorization": "AR-JWT token_here",
"content-type": "multipart/form-data; boundary=\"7572d1e8-7bd7-4f01-9c78-ce5b624faab3\""
}
Body:
--7572d1e8-7bd7-4f01-9c78-ce5b624faab3
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name=entry
{"Metadata":"abc"}
--7572d1e8-7bd7-4f01-9c78-ce5b624faab3
Content-Type: application/octet-stream
Content-Disposition: form-data; name=attach-z2AF_Act_Attachment_1
MjIyMg==
--7572d1e8-7bd7-4f01-9c78-ce5b624faab3--

CSharp - How can I upload an image using a HTTP Request and Multipart as the Content-Type

I am using C# 4.7.2 and am using a Console and not WinForms. I am trying to get an input of a user's image path then send a post request to a ShareX Image Hoster API.
How can I keep it plain and simple using void? EX:
public static void UploadImg(string ImagePath, string UploadAPI, string UploadKey) { }
ShareX Config:
{
"Version": "13.2.1",
"Name": "host",
"DestinationType": "ImageUploader",
"RequestMethod": "POST",
"RequestURL": "https://ADDRESS/upload",
"Headers": {
"token": "name_RANDOMSTRING",
"json": "true"
},
"Body": "MultipartFormData",
"Arguments": {
"imgfile": null
},
"FileFormName": "imgfile",
"URL": "$json:url$"
}
Capturing traffic with Fiddler I can use these headers:
POST https://IMAGEHOST/api/upload HTTP/1.1
token: SPECIALKEY
json: true
Content-Type: multipart/form-data; boundary=--------------------8d8ee229124e662
User-Agent: ShareX/13.4.0
Host: IMGHOSTER
Content-Length: 7518
Connection: Keep-Alive
----------------------8d8ee229124e662
Content-Disposition: form-data; name="imgfile"; filename="851TO25E8.png"
Content-Type: image/png
Then the rest after these headers is unknown ascii bytes nonsense.
The response is:
{"url":"https://FinalShortenedURL/‌​​​‌‌​‌​‌‌​‌‌‌‌‌‌​​​‌​‌‌‌​​​​​‌​‌​‌‌‌‌‌​​‌‌‌‌​​‌‌‌‌​‌‌‌‌‌​​"}
UPDATE - .Net 4.7.2
public static async Task UploadImg(string ImagePath, string UploadAPI, string UploadKey)
{
using (var client = new System.Net.Http.HttpClient())
{
// TODO: implement auth - this example works for bearer tokens:
// client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", UploadKey);
// Or you could use simple headers:
client.DefaultRequestHeaders.Add("token", UploadKey);
// inject the JSON header... and others if you need them
client.DefaultRequestHeaders.Add("json", "true");
var uri = new System.Uri(UploadAPI);
// Load the file:
var file = new System.IO.FileInfo(ImagePath);
if (!file.Exists)
throw new ArgumentException($"Unable to access file at: {ImagePath}", nameof(ImagePath));
using (var stream = file.OpenRead())
{
var multipartContent = new System.Net.Http.MultipartFormDataContent();
multipartContent.Add(
new System.Net.Http.StreamContent(stream),
"imgfile", // this is the name of FormData field
file.Name);
System.Net.Http.HttpRequestMessage request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Post, uri);
request.Content = multipartContent;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode(); // this throws an exception on non HTTP success codes
}
}
}
The following is the original posted solution for .Net Core to upload using multi-part:
public static async Task UploadImg(string ImagePath, string UploadAPI, string UploadKey)
{
using (var client = new Windows.Web.Http.HttpClient())
{
// TODO: implement auth - this example works for bearer tokens:
client.DefaultRequestHeaders.Authorization = new Windows.Web.Http.Headers.HttpCredentialsHeaderValue("Bearer", UploadKey);
// Or you could use simple headers:
client.DefaultRequestHeaders.Add("token", UploadKey);
// Load the file:
StorageFile file = await StorageFile.GetFileFromPathAsync(ImagePath);
var uri = new System.Uri(UploadAPI);
HttpMultipartFormDataContent multipartContent = new HttpMultipartFormDataContent();
multipartContent.Add(
new HttpStreamContent(stream),
"imgfile", // this is the name of FormData field
file.Name);
Windows.Web.Http.HttpRequestMessage request = new Windows.Web.Http.HttpRequestMessage(Windows.Web.Http.HttpMethod.Post, uri);
request.Content = multipartContent;
var response = await client.SendRequestAsync(request);
response.EnsureSuccessStatusCode(); // this throws an exception on non HTTP success codes
}
}
The process is similar in .Net framework, except that you can use System.IO for file operations.
More Information
Having a quick snoop around SO will find many similar questions with similar solutions or pratical advice. This answer is specifically provided to work with OPs ShareX configuration, but if you need further information have a read over these articles:
Http MultipartFormDataContent
C# HttpClient 4.5 multipart/form-data upload
c# MultipartFormDataContent Add methods (how to properly add a file)

Sending HTTP post request in C# to Microsoft Bing speech API

I am trying to send a HTTP post request to microsoft Bing speech API o transcribe an audio file. First we need to send a post request to get an "access token" as a response, then this token is used (as authorisation" in another post request to upload the actual file and get the transcription in the response. I can send the first post request and successfully get the access token, but I am not able to get a reasonable response for my second post request. I follow this page: https://www.microsoft.com/cognitive-services/en-us/speech-api/documentation/api-reference-rest/bingvoicerecognition
This is the second post request:
Guid requestId = Guid.NewGuid();
var Uri = #"https://speech.platform.bing.com/recognize?version=3.0&requestid=" + requestId.ToString() + #"&appID=D4D52672-91D7-4C74-8AD8-42B1D981415A&format=json&locale=en-US&device.os=Windows%20OS&scenarios=ulm&instanceid=f1efbd27-25fd-4212-9332-77cd63176112";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Uri);
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken));
request.Headers.TryAddWithoutValidation("Content-Type", #"audio/wav; samplerate=16000");
MemoryStream ms = new MemoryStream();
using (var fs = System.IO.File.OpenRead("audio.wav"))
{
byte[] buffer = new byte[1024 * 8];
while (fs.Read(buffer, 0, buffer.Length) > 0)
{
ms.Write(buffer, 0, buffer.Length);
}
fs.Close();
}
ms.Seek(0, SeekOrigin.Begin);
HttpContent _Body = new StreamContent(ms);
request.Content = _Body;
var client2 = new HttpClient();
var response2 = client2.SendAsync(request);
I guess the problem is where I set the "Content-Type" for the header. The reason is when I debug, I don't see this property being set in the Header of the request. In fact, there is no Content-Type in the header. Any help would be appreciated. This page, which talks about the equivalent curl command, can also be helpful: https://social.msdn.microsoft.com/Forums/en-US/ad73e4f1-e576-4080-9fe7-060cc2f583ca/microsoft-bing-voice-recognition-api-authorization-404resource-not-found?forum=SpeechService
Content-Type is a content related header. The following code works for me:
public async Task<string> SendRequestAsync(string url, string bearerToken, string contentType, string fileName)
{
var content = new StreamContent(File.OpenRead(fileName));
content.Headers.TryAddWithoutValidation("Content-Type", contentType);
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
var response = await httpClient.PostAsync(url, content);
return await response.Content.ReadAsStringAsync();
}
}
The invocation in your case (if you work in synchronous context):
var result = SendRequestAsync(Uri, accessToken, "audio/wav; samplerate=16000", "audio.wav").Result;
You can send the following header instead, to not have to do 2 requests because of the token.
If you want to not have to login each time instead of using the 'Authorization': 'Bearer {TOKEN}' header you could use the 'Ocp-Apim-Subscription-Key': '{YOUR AZURE TOKEN}' in order to not have to make a authorisation factory or more requests than necessary to the application and make it faster
NOTE: {TOKEN} is a JWT token like
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6Imh0dHBzOi8vc3BlZWNoLnBsYXRmb3JtLmJpbmcuY29tIiwic3Vic2NyaXB0aW9uLWlkIjoiZmFhZTNlYTkxNmI1NGMxZWEyODY4MDlhYTg3ZWE1MmUiLCJwcm9kdWN0LWlkIjoiQmluZy5TcGVlY2guUHJldmlldyIsImNvZ25pdGl2ZS1zZXJ2aWNlcy1lbmRwb2ludCI6Imh0dHBzOi8vYXBpLmNvZ25pdGl2ZS5taWNyb3NvZnQuY29tL2ludGVybmFsL3YxLjAvIiwiYXp1cmUtcmVzb3VyY2UtaWQiOiIiLCJpc3MiOiJ1cm46bXMuY29nbml0aXZlc2VydmljZXMiLCJhdWQiOiJ1cm46bXMuc3BlZWNoIiwiZXhwIjoxNTAwODgxNjIzfQ.KdlCrIJ_H0jxs1yyeyYxYR7ucbLuFKT__ep7lGJmGbU
NOTE2: {YOUR AZURE TOKEN} is like d5kals90935b40809dc6k38533c21e85 and you find it here
The request would look like this:
curl -v -X POST "https://speech.platform.bing.com/speech/recognition/interactive/cognitiveservices/v1?language=es-ES&locale=es-ES&format=simple&requestid=req_id" -H "Ocp-Apim-Subscription-Key: d5kals90935b40809dc6k38533c21e85" -H 'Transfer-Encoding: chunked' -H 'Content-type: audio/wav; codec="audio/pcm"; samplerate=8000' --data-binary #"{BINAYFILE}.wav"

"No file uploaded or URL provided" when calling ocr.space API

I'm trying to call this API from my C# app:
https://ocr.space/OCRAPI
When I call it from curl, it just works fine:
curl -k --form "file=#filename.jpg" --form "apikey=helloworld" --form "language=eng" https://api.ocr.space/Parse/Image
I implemented it this way:
[TestMethod]
public async Task Test_Curl_Call()
{
var client = new HttpClient();
String cur_dir = Directory.GetCurrentDirectory();
// Create the HttpContent for the form to be posted.
var requestContent = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>( "file", "#filename.jpg"), //I also tried "filename.jpg"
new KeyValuePair<string, string>( "apikey", "helloworld" ),
new KeyValuePair<string, string>( "language", "eng")});
// Get the response.
HttpResponseMessage response = await client.PostAsync(
"https://api.ocr.space/Parse/Image",
requestContent);
// Get the response content.
HttpContent responseContent = response.Content;
// Get the stream of the content.
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
// Write the output.
String result = await reader.ReadToEndAsync();
Console.WriteLine(result);
}
}
I get this answer :
{
"ParsedResults":null,
"OCRExitCode":99,
"IsErroredOnProcessing":true,
"ErrorMessage":"No file uploaded or URL provided",
"ErrorDetails":"",
"ProcessingTimeInMilliseconds":"0"
}
Any clue?
What's the # character for in "file=#filename.jpg"?
I put my filename.jpg file in the project and test project bin/debug directory and run my test project in debug mode.
So I don't think the error points to the file not being where expected.
I'd rather suspect a syntax error in my code.
The error message is telling you what's wrong:
No file uploaded or URL provided
You sent a filename to the service in your code, but that's not the same thing as giving curl a filename. curl is smart enough to read the file and upload the contents with your request, but in your C# code, you'll have to do that yourself. The steps will be:
Read the file bytes from disk.
Create a multipart request with two parts: the API key ("helloworld"), and the file bytes.
POST this request to the API.
Fortunately, it's pretty easy. This question demonstrates the syntax to set up a multipart request.
This code worked for me:
public async Task<string> TestOcrAsync(string filePath)
{
// Read the file bytes
var fileBytes = File.ReadAllBytes(filePath);
var fileName = Path.GetFileName(filePath);
// Set up the multipart request
var requestContent = new MultipartFormDataContent();
// Add the demo API key ("helloworld")
requestContent.Add(new StringContent("helloworld"), "apikey");
// Add the file content
var imageContent = new ByteArrayContent(fileBytes);
imageContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
requestContent.Add(imageContent, "file", fileName);
// POST to the API
var client = new HttpClient();
var response = await client.PostAsync("https://api.ocr.space/parse/image", requestContent);
return await response.Content.ReadAsStringAsync();
}

Categories

Resources