I need to download a file to byte[] using Selenium in C#.
The problem is the file is downloaded via a button which does a javascript call:
javascript:__doPostBack('ctl00$MainContent$gvOutputs','Select$0')
If i could get a URL I could just use the C# command:
using (WebClient wc = new WebClient())
{
wc.Headers[HttpRequestHeader.Cookie] = this.GetCookieHeaderString(); //Get Cookie from Selenium window
return wc.DownloadData(sourceURL);
}
Unfortunately this wont work as I dont have the URL.
the __dopostback makes a POST request which looks like this:
__EVENTTARGET=ctl00%24MainContent%24gvOutputs
__EVENTARGUMENT=Select%240
__VIEWSTATE=sEM2tcQczKVsK5kzEN2x19Gxco%....
__VIEWSTATEGENERATOR=B935C9B7
__VIEWSTATEENCRYPTED=
__EVENTVALIDATION=kOyxw5ZKBd1yygTXmUR%....
I suppose if there were a way to get those variables I could create a POST in C#.. However I'm not sure how I can get those variables?
I can click the link in selenium, but that will force a download to the clients computer..
I suppose 1 option would be to monitor the download directory, and read it this way, but im trying to avoid this brute force method.
I was hoping for a better answer. However I ended up making a specific DOWNLOAD path for every Selenium instance, then monitoring that directory and associating the files with the selenium instance.. A little bit hacky but it works.
I am a bit late, but I just found a solution for my case.
You can use this chunk of JS code to do the post request:
var xhr = new XMLHttpRequest();
xhr.open('POST', '<THE-POST-REQUEST-URL>', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
if (this.status === 200) {
var fileArray = this.response;
var filename = '';
var disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
}
var byteArray = new Uint8Array(fileArray);
var obj = {
'fileName': filename,
'fileData': byteArray
}
callback(obj);
}
else {
callback('server error - file might not be found');
}
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send('<POST-REQUEST-ARGUMENTS>');
and then call it in selenium:
var response = driver.ExecuteAsyncScript(
#$ "var callback = arguments[arguments.length - 1];
// The rest of the code from the last example
"
);
if (response is Dictionary < string, object > fileDict)
{
var fileName = fileDict["fileName"]?.ToString();
var fileData = fileDict["fileData"];
if (fileData is ReadOnlyCollection < object > readonlyCollection)
{
var byteArray = readonlyCollection.Select(i => (byte)(int)(long) i).ToArray();
var tempFile = "PATH/TO/FILE";
File.WriteAllBytes(tempFile, byteArray);
}
else
{
throw new Exception("fileData is not a byte array");
}
}
Related
I am building a video management app that allows for uploading multiple videos to Azure Storage which is then encoded by Azure Media Services.
My issue is that if I upload just 1 file at a time with blueimp, everything works fine. When I add more than one file to the upload, I get the error on the second file.
Unexpected end of MIME multipart stream. MIME multipart message is not complete.
I have read that it could be to the stream missing the end of file terminator, so I added in the suggested tweak to append the line terminator (per this article ASP.NET Web API, unexpected end of MIME multi-part stream when uploading from Flex FileReference) with no luck.
If I post as single files (by either iterating over the files selected for upload) and send them as individual posts, it works. My issue is that I want to select several files as well as add additional metadata and hit one submit button. When I do it this way, the first file uploads, and the second one appears to start to upload but then I get the error 500 "Unexpected end of MIME multipart stream. MIME multipart message is not complete" message.
Here is my upload code (Web API):
[HttpPost]
public async Task<HttpResponseMessage> UploadMedia()
{
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Headers["content-type"] != null)
{
httpRequest.Headers.Remove("content-type");
}
httpRequest.Headers.Add("enctype", "multipart/form-data");
if (httpRequest.Files.Count > 0)
{
var docfiles = new List<string>();
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
string assignedSectionList = string.Empty;
postedFile.SaveAs(filePath);
docfiles.Add(filePath);
string random = Helpers.Helper.RandomDigits(10).ToString();
string ext = System.IO.Path.GetExtension(filePath);
string newFileName = (random + ext).ToLower();
MediaType mediaType = MediaType.Video;
if (newFileName.Contains(".mp3"))
{
mediaType = MediaType.Audio;
}
if (httpRequest.Form["sectionList"] != null)
{
assignedSectionList = httpRequest.Form["sectionList"];
}
MediaUploadQueue mediaUploadQueueItem = new MediaUploadQueue();
mediaUploadQueueItem.OriginalFileName = postedFile.FileName;
mediaUploadQueueItem.FileName = newFileName;
mediaUploadQueueItem.UploadedDateTime = DateTime.UtcNow;
mediaUploadQueueItem.LastUpdatedDateTime = DateTime.UtcNow;
mediaUploadQueueItem.Status = "pending";
mediaUploadQueueItem.Size = postedFile.ContentLength;
mediaUploadQueueItem.Identifier = random;
mediaUploadQueueItem.MediaType = mediaType;
mediaUploadQueueItem.AssignedSectionList = assignedSectionList;
db.MediaUploadQueue.Add(mediaUploadQueueItem);
db.SaveChanges();
byte[] chunk = new byte[httpRequest.ContentLength];
httpRequest.InputStream.Read(chunk, 0, Convert.ToInt32(httpRequest.ContentLength));
var provider = new AzureStorageMultipartFormDataStreamProviderNoMod(new AzureMediaServicesHelper().container);
provider.fileNameOverride = newFileName;
await Request.Content.ReadAsMultipartAsync(provider); //this uploads it to the storage account
//AzureMediaServicesHelper amsHelper = new AzureMediaServicesHelper();
string assetId = amsHelper.CommitAsset(mediaUploadQueueItem); //begin the process of encoding the file
mediaUploadQueueItem.AssetId = assetId;
db.SaveChanges();
////start the encoding
amsHelper.EncodeAsset(assetId);
}
result = Request.CreateResponse(HttpStatusCode.Created, docfiles);
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
return result;
}
Here is the code for the upload handler that sends to Azure Blob storage
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
if (parent == null) throw new ArgumentNullException(nameof(parent));
if (headers == null) throw new ArgumentNullException(nameof(headers));
if (!_supportedMimeTypes.Contains(headers.ContentType.ToString().ToLower()))
{
throw new NotSupportedException("Only jpeg and png are supported");
}
// Generate a new filename for every new blob
var fileName = Guid.NewGuid().ToString();
if (!String.IsNullOrEmpty(fileNameOverride))
fileName = fileNameOverride;
CloudBlockBlob blob = _blobContainer.GetBlockBlobReference(fileName);
if (headers.ContentType != null)
{
// Set appropriate content type for your uploaded file
blob.Properties.ContentType = headers.ContentType.MediaType;
}
this.FileData.Add(new MultipartFileData(headers, blob.Name));
return blob.OpenWrite();
}
Here is the javascript code. The first one is sending the files individual as separate posts, it works.
$("#fileupload").fileupload({
autoUpload: false,
dataType: "json",
add: function (e, data) {
data.context = $('<p class="file">')
.append($('<a target="_blank">').text(data.files[0].name))
.appendTo(document.body);
data.submit();
},
progress: function (e, data) {
var progress = parseInt((data.loaded / data.total) * 100, 10);
data.context.css("background-position-x", 100 - progress + "%");
},
done: function (e, data) {
data.context
.addClass("done")
.find("a")
.prop("href", data.result.files[0].url);
}
});
This code below does not work. It pushes all the files into and array and sends them in one single post. This one fails on the second file. If I upload just one file using this code, it works.
var filesList = new Array();
$(function () {
$('#fileupload').fileupload({
autoUpload: false,
dropZone: $('#dropzone'),
add: function (e, data) {
filesList.push(data.files[0]);
data.context = $('<div class="file"/>', { class: 'thumbnail pull-left' }).appendTo('#files');
var node = $('<p />').append($('<span/>').text(data.files[0].name).data(data));
node.appendTo(data.context);
},
progress: function (e, data) { //Still working on this part
//var progress = parseInt((data.loaded / data.total) * 100, 10);
//data.context.css("background-position-x", 100 - progress + "%");
},
}).on('fileuploadprocessalways', function (e, data) {
var index = data.index,
file = data.files[index],
node = $(data.context.children()[index]);
if (file.preview) {
node.prepend('<br>').prepend(file.preview);
}
if (file.error) {
node.append('<br>').append($('<span class="text-danger"/>').text(file.error));
}
}).prop('disabled', !$.support.fileInput)
.parent().addClass($.support.fileInput ? undefined : 'disabled');
$("#uploadform").submit(function (event) {
if (filesList.length > 0) {
console.log("multi file submit");
event.preventDefault();
$('#fileupload').fileupload('send', { files: filesList })
.success(function (result, textStatus, jqXHR) { console.log('success'); })
.error(function (jqXHR, textStatus, errorThrown) { console.log('error'); })
.complete(function (result, textStatus, jqXHR) {
console.log('complete: ' + JSON.stringify(result)); //The error 500 is returned here. In fiddler, it shows and error 500. If I try to trap in Visual Studio, I can't seem to pinpoint the exception.
// window.location='back to view-page after submit?'
});
} else {
console.log("plain default form submit");
}
});
});
Any thoughts on why this would be happening? I have tried every approach I can think about with no luck. Thank you in advance!
I want to point out that the architecture of your code might cause timeouts or errors.
I would first upload everything to azure storage, storage the status in cache or database.
then I would fire a background job (hangfire, azure functions, webjobs) to process uploading to media service to do the other stuff.
I would suggest doing this asynchronously from the user input.
as per the documentation of dropzone make sure you add name in the HTML tag
<form action="/file-upload" class="dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
</div>
</form>
if you are doing it programatically:
function param() {
return "files";
}
Dropzone.options.myDropzone = {
uploadMultiple: true,
paramName: param,
}
on the backend you need to add \r\n after each stream:
below is C# WEB API Code to generate Excel :
public class FileExportController : ApiController
{
[HttpGet]
public HttpResponseMessage Get()
{
var callerContext = CallerContext.DefaultCallerContext;
ReportingInput userInput = new ReportingInput();
userInput.ClientOneCode = "AVON";
string handle = Guid.NewGuid().ToString();
var #event = new GetJobReprotDataBlEvent(callerContext, userInput);
WebApiApplication.ApplicationInitializerObj.EventBus.Publish(#event);
XLWorkbook wb = new FileExportEngine().ExportExcel(#event.ReportData); //this is returning XLWorkbook
string fileName = "JobReport_" + DateTime.Now.ToString("yyyy -MM-dd HH':'mm':'ss") + ".xlsx";
using (MemoryStream memoryStream = new MemoryStream())
{
wb.SaveAs(memoryStream);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(memoryStream.ToArray())
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
}
When I call this API from browser, I am able to generate excel file.
http://localhost/ETLScheduler/api/FileExport -- this is working when hit direct in browser
Now I want to use consume this API in angular 5 application.I have a button.On click button I call the component method downloadFile() to download the file.
Below is the code :
downloadReport() {
this._service.downloadJobReport('AVON');
}
where downloadJobReport() is in my service file as below :
downloadJobReport(clientCode: string) {
return this._http.get(APIs.downloadJobReport);
}
When I am running the application and click on Download button, I am getting nothing, I mean file is not downloading. Can anyone have idea,how should I update my angular code to consume the API.
Thanks in advance.
As you mentioned above in comments you are using below angular code to download file:
downloadFile(data: Blob) {
const contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
const blob = new Blob([data], { type: contentType });
const url = window.URL.createObjectURL(blob);
window.open(url);
}
As I also have tried this code, it is working in chrome browser but not working in IE and edge.
You may update your method somthing like below:
var downloadFile=function (file_name, content) {
var csvData = new Blob([content], { type: 'text/csv' });
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
window.navigator.msSaveOrOpenBlob(csvData, file_name);
} else { // for Non-IE (chrome, firefox etc.)
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var csvUrl = URL.createObjectURL(csvData);
a.href = csvUrl;
a.download = file_name;
a.click();
URL.revokeObjectURL(a.href)
a.remove();
}
};
you can refer below link for more information:
Open links made by createObjectURL in IE11
Problem is, that Angular expects JSON as a result. You need to configure your GET request so, that it expects something different.
public downloadJobReport(clientCode: string)): Observable<Blob> {
return this._http.get(APIs.downloadJobReport, { responseType: 'blob' });
}
Just a tiny question, you pass an argument clientCode to the downloadJobReport, but never use it. Maybe wise to leave that out?
I want to make the following curl call in my C# console application:
curl -d "text=This is a block of text" \
http://api.repustate.com/v2/demokey/score.json
I tried to do like the question posted here, but I cannot fill the properties properly.
I also tried to convert it to a regular HTTP request:
http://api.repustate.com/v2/demokey/score.json?text="This%20is%20a%20block%20of%20text"
Can I convert a cURL call to an HTTP request? If so, how? If not, how can I make the above cURL call from my C# console application properly?
Well, you wouldn't call cURL directly, rather, you'd use one of the following options:
HttpWebRequest/HttpWebResponse
WebClient
HttpClient (available from .NET 4.5 on)
I'd highly recommend using the HttpClient class, as it's engineered to be much better (from a usability standpoint) than the former two.
In your case, you would do this:
using System.Net.Http;
var client = new HttpClient();
// Create the HttpContent for the form to be posted.
var requestContent = new FormUrlEncodedContent(new [] {
new KeyValuePair<string, string>("text", "This is a block of text"),
});
// Get the response.
HttpResponseMessage response = await client.PostAsync(
"http://api.repustate.com/v2/demokey/score.json",
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.
Console.WriteLine(await reader.ReadToEndAsync());
}
Also note that the HttpClient class has much better support for handling different response types, and better support for asynchronous operations (and the cancellation of them) over the previously mentioned options.
Or in restSharp:
var client = new RestClient("https://example.com/?urlparam=true");
var request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("header1", "headerval");
request.AddParameter("application/x-www-form-urlencoded", "bodykey=bodyval", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Below is a working example code.
Please note you need to add a reference to Newtonsoft.Json.Linq
string url = "https://yourAPIurl";
WebRequest myReq = WebRequest.Create(url);
string credentials = "xxxxxxxxxxxxxxxxxxxxxxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
CredentialCache mycache = new CredentialCache();
myReq.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials));
WebResponse wr = myReq.GetResponse();
Stream receiveStream = wr.GetResponseStream();
StreamReader reader = new StreamReader(receiveStream, Encoding.UTF8);
string content = reader.ReadToEnd();
Console.WriteLine(content);
var json = "[" + content + "]"; // change this to array
var objects = JArray.Parse(json); // parse as array
foreach (JObject o in objects.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
string name = p.Name;
string value = p.Value.ToString();
Console.Write(name + ": " + value);
}
}
Console.ReadLine();
Reference: TheDeveloperBlog.com
Late response but this is what I ended up doing. If you want to run your curl commands very similarly as you run them on linux and you have windows 10 or latter do this:
public static string ExecuteCurl(string curlCommand, int timeoutInSeconds=60)
{
if (string.IsNullOrEmpty(curlCommand))
return "";
curlCommand = curlCommand.Trim();
// remove the curl keworkd
if (curlCommand.StartsWith("curl"))
{
curlCommand = curlCommand.Substring("curl".Length).Trim();
}
// this code only works on windows 10 or higher
{
curlCommand = curlCommand.Replace("--compressed", "");
// windows 10 should contain this file
var fullPath = System.IO.Path.Combine(Environment.SystemDirectory, "curl.exe");
if (System.IO.File.Exists(fullPath) == false)
{
if (Debugger.IsAttached) { Debugger.Break(); }
throw new Exception("Windows 10 or higher is required to run this application");
}
// on windows ' are not supported. For example: curl 'http://ublux.com' does not work and it needs to be replaced to curl "http://ublux.com"
List<string> parameters = new List<string>();
// separate parameters to escape quotes
try
{
Queue<char> q = new Queue<char>();
foreach (var c in curlCommand.ToCharArray())
{
q.Enqueue(c);
}
StringBuilder currentParameter = new StringBuilder();
void insertParameter()
{
var temp = currentParameter.ToString().Trim();
if (string.IsNullOrEmpty(temp) == false)
{
parameters.Add(temp);
}
currentParameter.Clear();
}
while (true)
{
if (q.Count == 0)
{
insertParameter();
break;
}
char x = q.Dequeue();
if (x == '\'')
{
insertParameter();
// add until we find last '
while (true)
{
x = q.Dequeue();
// if next 2 characetrs are \'
if (x == '\\' && q.Count > 0 && q.Peek() == '\'')
{
currentParameter.Append('\'');
q.Dequeue();
continue;
}
if (x == '\'')
{
insertParameter();
break;
}
currentParameter.Append(x);
}
}
else if (x == '"')
{
insertParameter();
// add until we find last "
while (true)
{
x = q.Dequeue();
// if next 2 characetrs are \"
if (x == '\\' && q.Count > 0 && q.Peek() == '"')
{
currentParameter.Append('"');
q.Dequeue();
continue;
}
if (x == '"')
{
insertParameter();
break;
}
currentParameter.Append(x);
}
}
else
{
currentParameter.Append(x);
}
}
}
catch
{
if (Debugger.IsAttached) { Debugger.Break(); }
throw new Exception("Invalid curl command");
}
StringBuilder finalCommand = new StringBuilder();
foreach (var p in parameters)
{
if (p.StartsWith("-"))
{
finalCommand.Append(p);
finalCommand.Append(" ");
continue;
}
var temp = p;
if (temp.Contains("\""))
{
temp = temp.Replace("\"", "\\\"");
}
if (temp.Contains("'"))
{
temp = temp.Replace("'", "\\'");
}
finalCommand.Append($"\"{temp}\"");
finalCommand.Append(" ");
}
using (var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "curl.exe",
Arguments = finalCommand.ToString(),
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Environment.SystemDirectory
}
})
{
proc.Start();
proc.WaitForExit(timeoutInSeconds*1000);
return proc.StandardOutput.ReadToEnd();
}
}
}
The reason why the code is a little bit long is because windows will give you an error if you execute a single quote. In other words, the command curl 'https://google.com' will work on linux and it will not work on windows. Thanks to that method I created you can use single quotes and run your curl commands exactly as you run them on linux. This code also checks for escaping characters such as \' and \".
For example use this code as
var output = ExecuteCurl(#"curl 'https://google.com' -H 'Accept: application/json, text/javascript, */*; q=0.01'");
If you where to run that same string agains C:\Windows\System32\curl.exe it will not work because for some reason windows does not like single quotes.
I know this is a very old question but I post this solution in case it helps somebody. I recently met this problem and google led me here. The answer here helps me to understand the problem but there are still issues due to my parameter combination. What eventually solves my problem is curl to C# converter. It is a very powerful tool and supports most of the parameters for Curl. The code it generates is almost immediately runnable.
Don't forget to add System.Net.Http, specially if you receive this error:
Severity Code Description Project File Line Suppression State
Error CS0246 The type or namespace name 'HttpClient' could not be
found (are you missing a using directive or an assembly
reference?) 1_default.aspx D:\Projetos\Testes\FacebookAPI\FB-CustomAudience\default.aspx.cs 56 Active
In this case you shoud:
Add System.Net.Http from Nuget: Tools / NuGet Package Manager / Manager NuGet Packages for Solution;
Search for System.Net.Http
Add in the top of your page the follow code:
using System.Net.Http;
Call cURL from your console app is not a good idea.
But you can use TinyRestClient which make easier to build requests :
var client = new TinyRestClient(new HttpClient(),"https://api.repustate.com/");
client.PostRequest("v2/demokey/score.json").
AddQueryParameter("text", "").
ExecuteAsync<MyResponse>();
Well if you are new to C# with cmd-line exp. you can use online sites like "https://curl.olsh.me/" or search curl to C# converter will returns site that could do that for you.
or if you are using postman you can use Generate Code Snippet only problem with Postman code generator is the dependency on RestSharp library.
I want to make the following curl call in my C# console application:
curl -d "text=This is a block of text" \
http://api.repustate.com/v2/demokey/score.json
I tried to do like the question posted here, but I cannot fill the properties properly.
I also tried to convert it to a regular HTTP request:
http://api.repustate.com/v2/demokey/score.json?text="This%20is%20a%20block%20of%20text"
Can I convert a cURL call to an HTTP request? If so, how? If not, how can I make the above cURL call from my C# console application properly?
Well, you wouldn't call cURL directly, rather, you'd use one of the following options:
HttpWebRequest/HttpWebResponse
WebClient
HttpClient (available from .NET 4.5 on)
I'd highly recommend using the HttpClient class, as it's engineered to be much better (from a usability standpoint) than the former two.
In your case, you would do this:
using System.Net.Http;
var client = new HttpClient();
// Create the HttpContent for the form to be posted.
var requestContent = new FormUrlEncodedContent(new [] {
new KeyValuePair<string, string>("text", "This is a block of text"),
});
// Get the response.
HttpResponseMessage response = await client.PostAsync(
"http://api.repustate.com/v2/demokey/score.json",
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.
Console.WriteLine(await reader.ReadToEndAsync());
}
Also note that the HttpClient class has much better support for handling different response types, and better support for asynchronous operations (and the cancellation of them) over the previously mentioned options.
Or in restSharp:
var client = new RestClient("https://example.com/?urlparam=true");
var request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("header1", "headerval");
request.AddParameter("application/x-www-form-urlencoded", "bodykey=bodyval", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Below is a working example code.
Please note you need to add a reference to Newtonsoft.Json.Linq
string url = "https://yourAPIurl";
WebRequest myReq = WebRequest.Create(url);
string credentials = "xxxxxxxxxxxxxxxxxxxxxxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
CredentialCache mycache = new CredentialCache();
myReq.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials));
WebResponse wr = myReq.GetResponse();
Stream receiveStream = wr.GetResponseStream();
StreamReader reader = new StreamReader(receiveStream, Encoding.UTF8);
string content = reader.ReadToEnd();
Console.WriteLine(content);
var json = "[" + content + "]"; // change this to array
var objects = JArray.Parse(json); // parse as array
foreach (JObject o in objects.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
string name = p.Name;
string value = p.Value.ToString();
Console.Write(name + ": " + value);
}
}
Console.ReadLine();
Reference: TheDeveloperBlog.com
Late response but this is what I ended up doing. If you want to run your curl commands very similarly as you run them on linux and you have windows 10 or latter do this:
public static string ExecuteCurl(string curlCommand, int timeoutInSeconds=60)
{
if (string.IsNullOrEmpty(curlCommand))
return "";
curlCommand = curlCommand.Trim();
// remove the curl keworkd
if (curlCommand.StartsWith("curl"))
{
curlCommand = curlCommand.Substring("curl".Length).Trim();
}
// this code only works on windows 10 or higher
{
curlCommand = curlCommand.Replace("--compressed", "");
// windows 10 should contain this file
var fullPath = System.IO.Path.Combine(Environment.SystemDirectory, "curl.exe");
if (System.IO.File.Exists(fullPath) == false)
{
if (Debugger.IsAttached) { Debugger.Break(); }
throw new Exception("Windows 10 or higher is required to run this application");
}
// on windows ' are not supported. For example: curl 'http://ublux.com' does not work and it needs to be replaced to curl "http://ublux.com"
List<string> parameters = new List<string>();
// separate parameters to escape quotes
try
{
Queue<char> q = new Queue<char>();
foreach (var c in curlCommand.ToCharArray())
{
q.Enqueue(c);
}
StringBuilder currentParameter = new StringBuilder();
void insertParameter()
{
var temp = currentParameter.ToString().Trim();
if (string.IsNullOrEmpty(temp) == false)
{
parameters.Add(temp);
}
currentParameter.Clear();
}
while (true)
{
if (q.Count == 0)
{
insertParameter();
break;
}
char x = q.Dequeue();
if (x == '\'')
{
insertParameter();
// add until we find last '
while (true)
{
x = q.Dequeue();
// if next 2 characetrs are \'
if (x == '\\' && q.Count > 0 && q.Peek() == '\'')
{
currentParameter.Append('\'');
q.Dequeue();
continue;
}
if (x == '\'')
{
insertParameter();
break;
}
currentParameter.Append(x);
}
}
else if (x == '"')
{
insertParameter();
// add until we find last "
while (true)
{
x = q.Dequeue();
// if next 2 characetrs are \"
if (x == '\\' && q.Count > 0 && q.Peek() == '"')
{
currentParameter.Append('"');
q.Dequeue();
continue;
}
if (x == '"')
{
insertParameter();
break;
}
currentParameter.Append(x);
}
}
else
{
currentParameter.Append(x);
}
}
}
catch
{
if (Debugger.IsAttached) { Debugger.Break(); }
throw new Exception("Invalid curl command");
}
StringBuilder finalCommand = new StringBuilder();
foreach (var p in parameters)
{
if (p.StartsWith("-"))
{
finalCommand.Append(p);
finalCommand.Append(" ");
continue;
}
var temp = p;
if (temp.Contains("\""))
{
temp = temp.Replace("\"", "\\\"");
}
if (temp.Contains("'"))
{
temp = temp.Replace("'", "\\'");
}
finalCommand.Append($"\"{temp}\"");
finalCommand.Append(" ");
}
using (var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "curl.exe",
Arguments = finalCommand.ToString(),
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Environment.SystemDirectory
}
})
{
proc.Start();
proc.WaitForExit(timeoutInSeconds*1000);
return proc.StandardOutput.ReadToEnd();
}
}
}
The reason why the code is a little bit long is because windows will give you an error if you execute a single quote. In other words, the command curl 'https://google.com' will work on linux and it will not work on windows. Thanks to that method I created you can use single quotes and run your curl commands exactly as you run them on linux. This code also checks for escaping characters such as \' and \".
For example use this code as
var output = ExecuteCurl(#"curl 'https://google.com' -H 'Accept: application/json, text/javascript, */*; q=0.01'");
If you where to run that same string agains C:\Windows\System32\curl.exe it will not work because for some reason windows does not like single quotes.
I know this is a very old question but I post this solution in case it helps somebody. I recently met this problem and google led me here. The answer here helps me to understand the problem but there are still issues due to my parameter combination. What eventually solves my problem is curl to C# converter. It is a very powerful tool and supports most of the parameters for Curl. The code it generates is almost immediately runnable.
Don't forget to add System.Net.Http, specially if you receive this error:
Severity Code Description Project File Line Suppression State
Error CS0246 The type or namespace name 'HttpClient' could not be
found (are you missing a using directive or an assembly
reference?) 1_default.aspx D:\Projetos\Testes\FacebookAPI\FB-CustomAudience\default.aspx.cs 56 Active
In this case you shoud:
Add System.Net.Http from Nuget: Tools / NuGet Package Manager / Manager NuGet Packages for Solution;
Search for System.Net.Http
Add in the top of your page the follow code:
using System.Net.Http;
Call cURL from your console app is not a good idea.
But you can use TinyRestClient which make easier to build requests :
var client = new TinyRestClient(new HttpClient(),"https://api.repustate.com/");
client.PostRequest("v2/demokey/score.json").
AddQueryParameter("text", "").
ExecuteAsync<MyResponse>();
Well if you are new to C# with cmd-line exp. you can use online sites like "https://curl.olsh.me/" or search curl to C# converter will returns site that could do that for you.
or if you are using postman you can use Generate Code Snippet only problem with Postman code generator is the dependency on RestSharp library.
As a follow-up to (OAuthException) (#15) The method you are calling must be called with an app secret signed session I want to know what is the equivalent of file_get_contents(). I tried the following but I got illegal characters in path error.
public ActionResult About()
{
var fb = new FacebookWebClient(FacebookWebContext.Current);
var tokenUrl = "https://graph.facebook.com/oauth/access_token?client_id=" + FacebookWebContext.Current.Settings.AppId + "&client_secret=" + FacebookWebContext.Current.Settings.AppSecret + "&grant_type=client_credentials";
var objReader = new StreamReader(tokenUrl);
string sLine = "";
var arrayList = new ArrayList();
while (sLine != null)
{
sLine = objReader.ReadLine();
if (sLine != null)
arrayList.Add(sLine);
}
objReader.Close();
var appToken = arrayList.ToString();
dynamic result = fb.Post(string.Format("{0}/accounts/test-users", FacebookWebContext.Current.Settings.AppId),
new { installed = false, permissions = "read_stream", access_token = appToken });
return Content(result.ToString());
}
I also tried System.IO.File.ReadAllText(tokenUrl) and I got the same error. Is there anything I can do?
I'm not even sure it's going to work, but at least I can try...
You can use WebClient.DownloadString to download text from a URL. The WebClient also supports authentication.
Also, to split your string into lines you can use:
string test;
string[] lines = test.Split('\n');
To use oauth/access_token or any methods related to oauth stuffs use FacebookOAuthClient not FacebookClient or FacebookClient.
FacebookOAuthClient.GetApplicationAccessToken(..)
FacebookOAuthClient.ExchangeCodeForAccessToken(..)