Shell curl requests

Hello ladies and gentlemen,

I registered on https://api-ninjas.com a free account which includes 50k free API requests to use Image2Text API.

In order to use it, I created a “Shell” object and this is the code that I’m working with, in my application:

Shell3.ExecuteMode = Shell.ExecuteModes.Asynchronous

Shell3.Execute("curl -X ""POST"" -H ""Content-Type:image/png"" -H ""X-Api-Key:" + App.ap1key + """ --data-binary ""image@temp.png"" https://api.api-ninjas.com/v1/imagetotext")

I’m using Shell.Execute and curl also in other parts of my program where it is working totally fine. Just in this case, I experienced a lot of issues.

I can tell from the response that I receive that I’m at least successfully authenticated, but I get a JSON response with this error message: Text could not be extracted from your image. Please select another image or try again.

This message is complete bullsh*t because I verified it online on their website and the API is totally capable of extracting text from exact that same image.

Here on this page, you can upload an image and try the API service for free. But you could also just register a free account with 50k requests for free without subscription.

If anybody could help me solve this problem, I’d be very thankful.

By the way, I think it has something to do with the curl command and its parameters, or the developer of said API is just a fool.

Also, I could verify that -d parameter for curl takes files with absolute and relative path. People claimed that on Stackoverflow and other users just confirmed that it is true.

If it’s necessary, I’ll create a test project in Xojo, and upload it here, so you can try it on your own.

Use --form rather than --data-binary. The API expects a multipart/form-data-encoded form with one file element named “image”, not the string “image” followed by the raw binary image data.

-X POST can be removed since --form implies POST.

Also -H "Content-Type: image/png" can be removed since the content type is dictated by --form.

Something like this:

curl -H "X-Api-Key: xxxxxxxxxxxxx" --form image=@temp.png https://api.api-ninjas.com/v1/imagetotext

P.S.

I wrote a Xojo wrapper for libcurl (the library component of curl) that is much more flexible and easy to use/debug than using a shell to call the command line tool. Here’s the equivalent of the above:

  Dim c As New cURLClient
  c.RequestHeaders.SetHeader("X-Api-Key", "xxxxxxxxxxxxxx")
  Dim img As FolderItem = SpecialFolder.Desktop.Child("temp.png")
  Dim form As New Dictionary
  form.Value("image") = img
  If c.Post("https://api.api-ninjas.com/v1/imagetotext", form) = True Then
    Dim result As String = c.GetDownloadedData
  End If

</spam>

I tried to use your libcurl library but I get the following error when I try to compile my software.

Click here.

I just needed to copy the DEFAULT_CA_INFO_PEM file to my project.

But now there’s a runtime exception:

I am using: curl 7.55.1 (Windows) libcurl/7.55.1 WinSSL

This suggests that one or more of the required DLLs was not found. Try downloading the latest version and its dependencies from this page: curl for Windows

Many of the requests you make with CURL can be done using URLConnection

I like tinkering with APIs so I built out an example project for how to do this in Xojo code with URLConnection. You are free to use it in any way you wish.

I may add more of the endpoints later, or if you’re interested in a consulting contract I can finish the implementation for all endpoints – send me an email workshop@timi.me

2 Likes

Hello Andrew,

I have downloaded curl binaries from here.

How can I use a relative path to the curl.exe and libcurl.dll file in my Xojo project? I have your wrapper/library in my Xojo project. Where can I set a relative path to the curllib.dll and curl.exe, so that your library uses this specific files to work with?

The DLLs must be located in the same directory as your executable.

Alternatively, you can specify the directory to look for them in by calling the SetDllDirectory function in the WinAPI:

Declare Function SetDllDirectoryW Lib "Kernel32" (Path As WString) As Boolean
Call SetDllDirectoryW("C:\Foo\Bar\")

After placing all dlls and curl.exe inside my binary folder, I get a NilObjectException instead of UnsupportedPlattformException.

Have you tried my demo project? All you need to do is drop in your API key and it’s good to go :slight_smile:

Can you screenshot the nilobjectexception?

Hello Tim,

I will check now.

If you’re looking for another example @Andrew_Lambert has added the TextLanguage endpoint :smiley:

Sorry for the late response, gentlemen. I decided against using the api-ninjas API endpoint because I get not very accurate results out of it, and it wouldn’t fit my use case at all if the results weren’t accurate.

Anyway, I created a Feedback case for an official cURL Xojo wrapper, or at least a better version of URLConnection. URLConnection is missing some basic features, as we all found out, especially concerning file transmission via POST. Unfortunately, there’s so much more that could be improved in URLConnection class.

Sadly, the Xojo documentation of URLConnection doesn’t look like being maintained in the last couple of months. On the page, it says: “* This page was last edited on 10 March 2021, at 18:09”. :frowning:

As I already said in the Feedback case, URLConnection is supposed to be a high-level abstract class that should make one’s life easier, not harder. Maybe you could support my idea for a better, updated URLConnection class? I think it would be a lot better. Not only for me but for everyone.

1 Like

URLConnection is missing some basic features, as we all found out, especially concerning file transmission via POST

Could you be more specific? We use URLConnection extensively in-house for connecting to all kinds of APIs and have yet to find one that doesn’t work.

Sadly, the Xojo documentation of URLConnection doesn’t look like being maintained in the last couple of months. On the page, it says: “* This page was last edited on 10 March 2021, at 18:09”.

All that means is that there haven’t been any changes worth noting in the docs. We do not go and mark each page updated with every release.

This is not the answer you expected. But this is a problem I’m facing right now with URLConnection and API endpoints.

This is part of my application. When I click PushButton2Captcha it will use Shell2 with a curl command to save a picture in the current working directory. After that, it will take the picture and upload it to 2captcha.com API endpoint.

Var picture As New Picture(560, 118)

picture = picture.Open(SpecialFolder.CurrentWorkingDirectory.Child("temp.png"))

ImageViewer1.Image = scaleImage(picture, 560, 118)



Var HTMLForm As New Dictionary
HTMLForm.Value("file") = SpecialFolder.CurrentWorkingDirectory.Child("temp.png")
HTMLForm.Value("key") = App.ap1key.DefineEncoding(Encodings.UTF8)

App.SetFormData(hsock, HTMLForm, " ") ' pass an empty boundary to generate a new one
hsock.Send("POST", App.ap1_url)

Then hsock ContentReceived event is triggered. It will save the received ID in App.captcha constant.

Var receivedContent As String = content.DefineEncoding(Encodings.UTF8)
Var sid() As String = receivedContent.Split("|")
App.captcha = sid()
Break

Then Timer1 will be executed every 10 seconds and will pass the captured ID saved in App.captcha to the second API endpoint of 2captcha.com.

If App.captcha.Count <> 0 Then
  If App.captcha(App.captcha.LastIndex).ToText.IndexOf("ERROR") < 0 Then
    hsock2.Send("GET", App.ap1_url_solved + "?key=" + App.ap1key.DefineEncoding(Encodings.UTF8) + "&action=get&id=" + App.captcha(App.captcha.LastIndex))
  End If
End If

hsock2 ContentReceived event will be triggered which then will put the result string in TextField3:

Var receivedContent As String = content.DefineEncoding(Encodings.UTF8)
App.captcha_res = receivedContent.Split("|")

If App.captcha.Count <> 0 Then
  If App.captcha_res(App.captcha_res.LastIndex).ToText.IndexOf("ERROR") < 0 AND App.captcha_res(App.captcha_res.LastIndex).ToText.IndexOf("NOT_READY") < 0 Then
    TextField3.Text = App.captcha_res(App.captcha_res.LastIndex).ToText
    App.captcha.RemoveAll
    App.captcha_res.RemoveAll
  End If
End If

So the first time I click the button PushButton2Captcha it works perfectly fine. But when I click the button a second time, I get “ERROR_WRONG_USER_KEY”.

2captcha.com API documentation says: “You’ve provided key parameter value in incorrect format, it should contain 32 symbols.”

I checked App.ap1key, and it is 32 characters long and is not in a wrong format.

So what is the problem here?

Since you are trying to reuse a socket, I suggest that you call ClearRequestHeaders each time to make sure there’s no cruft left over from the last call.

https://documentation.xojo.com/api/networking/urlconnection.html#urlconnection-clearrequestheaders

3 Likes

Thank you, I will try it in a couple of minutes.

You are an angel from heaven! Could you please add this information to the documentation for URLConnection? Maybe here and here? Just a quick note to let everyone know.

I assumed that the request headers are cleared after .Send() is called. Wouldn’t it be better to pass a boolean value like TRUE or 1 to the constructor of the URLConnection class if you want to keep the request header data?