problem with network.request() using POST

[lua]

local body = “language=en&apikey=yourapikeynoquotes”

local params.body = body

network.upload(“http://api.ocrapiservice.com/1.0/rest/ocr”,  “POST”,  networkListener,  params, “ocr1.jpg”,  system.ResourceDirectory, “image/jpeg”)

[/lua]

try that.

getting a syntax error now:

Syntax error

/Users/John/Library/Application Support/Outlaw/Sandbox/3/main.lua:37: unexpected symbol near ‘.’

 

-- create network request local body = "language=en&apikey=myapikey" local params.body = body network.upload("http://api.ocrapiservice.com/1.0/rest/ocr", "POST", networkListener, params, "ocr1.jpg", system.ResourceDirectory, "image/jpeg")

Actually, the reason the file upload fails is because your resource file inside of the APK, which is really a zip file.  Our network API on Android currently does not support uploading files inside of the APK (ie: your ResourceDirectory).

That said, there is a way to “trick” Corona into extracting your image file.  You have to do the following:

  1. Change the extension of your image from *.jpg to *.jpg_ (notice the underscore).

  2. Call system.pathForFile() on that image file.  Corona will automatically extract the image file to a hidden temporary directory.   Ignore the returned path.  You don’t need it.

  3. Finally, call network.upload() to upload your image file using the ResourceDirectory as the base. Do *not* use the returned path from the above function.

Wow.  That I would have never known.  Is there a different place I can put the file for the time being for testing purposes that’s a little more simple?

Also change line 36 to:

local params = {}

params.body = “language=en&apikey=myapikey”

Still giving me errors… Here’s what I have

– create network request
local params = {}
params.body = “language=en&apikey=myapikey”

– for testing
system.pathForFile(“ocr1.jpg_”)
– end for testing

network.upload(“http://api.ocrapiservice.com/1.0/rest/ocr”, “POST”, networkListener, params, “ocr1.jpg_”, system.ResourceDirectory, “image/jpeg”)

and I’m getting the error:
WARNING: Cannot create path for resource file ‘ocr1.jpg_’. File does not exist.

Runtime error

…repo5/main/plugins/network/mac/…/shared/network.lua:113: attempt to index field ‘body’ (a string value)

stack traceback:

…repo5/main/plugins/network/mac/…/shared/network.lua:113: in function ‘upload’

…ibrary/Application Support/Outlaw/Sandbox/3/main.lua:42: in function ‘_onEvent’

?: in function <?:931>

?: in function <?:218>

Oh your service is making this a challenge…  Our upload function doesn’t want to pass additional parameters to the host via the body structure and you have to use the body parameter to send in the API key and the language.  I think you were more on the right track (since we can’t pass additional post parameters using the network.upload() call)

So lets go back to your original code (and if you’re on Android and need to test from the Resource directory, you will need to follow Josh’s steps above too.

You need to read the data in from the file, not just open it.  Then you will need to base64 encode the data.

[lua]

local function _b64enc( data )
    – character table string
    local b=‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/’

    return ( (data:gsub( ‘.’, function( x )
        local r,b=’’, x:byte()
        for i=8,1,-1 do r=r … ( b % 2 ^ i - b % 2 ^ ( i - 1 ) > 0 and ‘1’ or ‘0’ ) end
        return r;
    end ) …‘0000’ ):gsub( ‘%d%d%d?%d?%d?%d?’, function( x )
        if ( #x < 6 ) then return ‘’ end
        local c = 0
        for i = 1, 6 do c = c + ( x:sub( i, i ) == ‘1’ and 2 ^ ( 6 - i ) or 0 ) end
        return b:sub( c+1, c+1 )
    end) … ( { ‘’, ‘==’, ‘=’ } )[#data %3 + 1] )
end

     local path = system.pathForFile(“ocr1.jpg”, system.ResourceDirectory)

     local file, errStr = io.open( path, “rb” )

     if file then

            local content = file:read( “*a” )

            local img = _b64enc(content)

    

            local headers = {}

            headers[“Content-Type”] = “multipart/form-data”

            local body = “image=” … img … “&language=” … language … “&apikey=” … ocrapiserviceApiKey

            

            local params = {}

            params.headers = headers

            params.body = body

            network.request(“http://api.ocrapiservice.com/1.0/rest/ocr”, “POST”, networkListener, params)

     end

[/lua]

Same error again!  Error 400.  This is the most frustrating thing I’ve ever dealt with.  I don’t know if it’s Corona or the ocr api that is a mess.  If I had to guess, it’s probably the ocr api.

Ok!  So I decided to scrap that ocrapiservice and move straight to an aws ec2 tesseract ocr solution.  I’ve tested it in my web browser by passing this:

myaddress.amazonaws.com/?img=http://www.alink.com/to/sometext.jpg

It gives me back an expected result using this php code server side:

\<?php $img = $\_REQUEST['img']; $callback = $\_REQUEST['callback']; $format = $\_REQUEST['format']; $tmpFile = uniqid(); file\_put\_contents("tmp/$tmpFile",file\_get\_contents($img)); $cmd = "/usr/local/bin/tesseract tmp/$tmpFile tmp/$tmpFile"; exec($cmd); @unlink("tmp/$tmpFile"); $res = file\_get\_contents("tmp/$tmpFile.txt"); @unlink("tmp/$tmpFile.txt"); switch($format) { case 'json': default: header('Content-type: application/json'); $json = json\_encode( array("success" =\> true, "img" =\> $img, "value" =\> $res) ); if ($callback != '') { print $callback . "(" . $json . ")"; } else { print $json; } break; case 'raw': case 'txt': print $res; break; } ?\>

Now, I’m still unclear if network.request or network.upload is the appropriate function to interact with this server side script, a little advice please?  Thanks!

The URL string you are passing is assuming the file to be processed is located on a website somewhere and you are accessing via a URL.   PHP’s file_get_contents() knows how to fetch a file from a web server and give you it’s contents.

To make this work, you are still going to have to upload the image from your device to a webserver somewhere then point your script at the webserver to run.

This looks like you have the ability to do your own PHP is that correct?

Yes, I have full control over the server side and can run any application on it.  I just have to figure out how to upload the image to the server and send a result as some sort of data back to the corona app.  All the heavy lifting will happen server side.

Basically I need to:

take a photo from the corona app (using a sample image for now)

upload that image to the server

process the ocr

return the result as data to the corona app

Thanks!

@Rob Miracle I was wondering if I could get your advice on something in regards to the PHP uploading.

Currently in the mod_parse module, you can upload a file to the Parse servers using just network.upload() with out any tricky handling, and it will upload the file perfectly.

Of course I’m not privy to how it was implemented server-side, but I’m trying desperately to do the same using a PHP script, which happens to work flawlessly when using a web form. So my question is about how the internals of the network.upload() work. My guess so far is that the body of the request is filled with either the binary or textual data, and that data alone. In that case I would assume using file_get_contents( ‘php://input’ ) should work. But alas, that does not contain any data. So where is it hiding? I can read from ‘php://input’ on other pages, so I am fairly confident it’s working.

I have also tried dumping $_FILES, $_POST, and $_REQUEST and all are empty. I assume that to be correct if the data is just being uploaded in a “raw” format?

I’ve been bashing my head with this for over a day now and am baffled. The thing that gets me is that it works for Parse, so what is it that I’m missing? Or do I just have deal with it manually via network.request() and base64 encode/decode in order to make it work for PHP? And is that any more costly than the standard network.upload()?

This really would be a useful thing to know, even on a stock Apache/PHP setup. Thanks in advance.

Cheers.

@develephant, We were going to do a tutorial on uploading files, but we ran into some technical problems and back burnered it. If I remember correctly, network.upload() does an HTTP PUT request and the $_FILES[] array should hold the uploaded info.  Some of the problems I ran into had to do with not being able to get the filename there.  I had to make up my own header entry for the file name and coax the PHP script to read it. 

The way most PHP scripts work wants to use the mime multi-part method.  You would use network.request() for this and I used the multipart class out of the old community code library. 

But what may be the problem is a setup issue on the PHP side.  If you google “empty $_POST”, there will be several posts that offer suggestions that might get you moving in the right direction.

@anthony113, you need to look up PHP Multipart Form uploads and see if that gets you started.

Rob

@Rob Miracle Thanks.  I will look into the PUT option.

I’m curious then what the use case is for network.upload() when it does not seem to jive with one of the major web scripting engines simply.

Thanks again.

I thought you said you could use it to upload to parse?  You can use PHP but you have to open the stream to get the data.  The code on the server side looks something like:

&nbsp;&nbsp;&nbsp; /\* PUT data comes in on the stdin stream \*/ &nbsp;&nbsp;&nbsp; $putdata = fopen("php://input", "r"); &nbsp;&nbsp;&nbsp; if ($putdata) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /\* Open a file for writing \*/ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $tmpfname = tempnam("upload", "myapp"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $fp = fopen($tmpfname, "w"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($fp) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /\* Read the data 1 KB at a time and write to the file \*/ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while ($data = fread($putdata, 1024)) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fwrite($fp, $data); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /\* Close the streams \*/ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fclose($fp); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fclose($putdata); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rename($tmpfname, "upload/" . $filename);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header("HTTP/1.1 201 Created"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo("File Created"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header("HTTP/1.1 403 Could not open tmp file." . $tmpfname); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo("Could not open tmp file " . $tmpfname); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; }

That should work with network.upload().  But if you notice I have a variable $filename.  That name is not natively passed to the server with this method. I had to make up my own file name system.

Now I haven’t used that code in a long time, so there are no warranties expressed or implied.  Your mileage may vary.  It’s completely unsupported.

@Rob Miracle First, thank you for sharing that code.  It creates the file, but the size is 0, so it’s got to be a config issue on my side.  I’ve probably read hundreds of pages on the issue, but still no luck.  I’m not running fcgi, it’s just straight apache mod.  pnp.ini is letting all the post vars though, etc.

And yes, network.upload() works flawlessly for Parse.  It would just be nice to know how that could work for us PHP users. :slight_smile:

I’m going to try a few other builds to see if I can get some action.  Thanks again, I appreciate you taking the time to reply.

Cheers.

@develephant - Thanks for trying to find a solution that will work. It will take the impossible out of the equation for us non-PHP wizards.

I just spent a little bit verifying this works – at least on my personal web host.  I’ve pinged engineering to get their blessing on the complete script and if they are okay with it, I’ll post the full script that does some filename checking, making sure the file isn’t too large, returns a full complement of errors etc.  Sounds like it could be a good Tuesday tutorial, though I have like 3-4 weeks of those queue up in front of it.

It’s possible I didn’t paste in everything needed, but I would suspect it might be a hosting issue.  I didn’t have to do anything (my personal test site is hosted with Hostgator). 

I am definitely looking forward to this tutorial. I tried this sample code (https://developer.coronalabs.com/code/upload-binary-corona-php-script) and it works when I try uploading a image (jpg).

To add on, that code does not provide a way to send in the filename together with it. So, you will need to modify it, so that the filename is sent together with the HTTP header, so the PHP-server side knows what filename it should be.

PHP-server side :

\<?php // Used when this function in PHP does not exist function apache\_request\_headers() { $arh = array(); $rx\_http = '/\AHTTP\_/'; foreach($\_SERVER as $key =\> $val) { if( preg\_match($rx\_http, $key) ) { $arh\_key = preg\_replace($rx\_http, '', $key); $rx\_matches = array(); // do some nasty string manipulations to restore the original letter case // this should work in most cases $rx\_matches = explode('\_', $arh\_key); if( count($rx\_matches) \> 0 and strlen($arh\_key) \> 2 ) { foreach($rx\_matches as $ak\_key =\> $ak\_val) $rx\_matches[$ak\_key] = ucfirst($ak\_val); $arh\_key = implode('-', $rx\_matches); } $arh[$arh\_key] = $val; } } return( $arh ); } // Get filename from headers $headers = apache\_request\_headers(); $filenameInHeader = 'FILENAME'; $filename = $headers[$filenameInHeader] ; $pngdata = $HTTP\_RAW\_POST\_DATA; if( isset( $pngdata ) ) { $img\_file = fopen ($filename, 'wb'); fwrite ($img\_file, base64\_decode( $pngdata )); fclose ($img\_file); echo "Success."; } else { echo "Failure."; } ?\>

Corona side :

local filenameToPhp = "yourfilenamehere.jpg" local function uploadBinary ( filename, url, onComplete ) local mime = require "mime" local path = system.pathForFile( filename ) local fileHandle = io.open( path, "rb" ) if fileHandle then local params = { headers = { ["Content-Type"] = "multipart/text", ["Filename"] = filenameToPhp, }, body = mime.b64( fileHandle:read( "\*a" ) ), progress = "upload", } io.close( fileHandle ) local function networkListener ( event ) print("main networkListener phase : " , event.phase, event.isError , event.response ) if event.phase == "began" then print( "main event.bytesEstimated : " , event.bytesEstimated ) elseif event.phase == "progress" then print( "main event.bytesEstimated, event.bytesTransferred : " , event.bytesEstimated, event.bytesTransferred ) elseif event.phase == "ended" then if (onComplete) then onComplete(event); end end return true; end network.request( url, "POST", networkListener, params) end end uploadBinary ( "localFilenameToUpload.jpg", "http://www.yoursite.com/uploadFile.php" , nil )

@Rob Miracle I was wondering if I could get your advice on something in regards to the PHP uploading.

Currently in the mod_parse module, you can upload a file to the Parse servers using just network.upload() with out any tricky handling, and it will upload the file perfectly.

Of course I’m not privy to how it was implemented server-side, but I’m trying desperately to do the same using a PHP script, which happens to work flawlessly when using a web form. So my question is about how the internals of the network.upload() work. My guess so far is that the body of the request is filled with either the binary or textual data, and that data alone. In that case I would assume using file_get_contents( ‘php://input’ ) should work. But alas, that does not contain any data. So where is it hiding? I can read from ‘php://input’ on other pages, so I am fairly confident it’s working.

I have also tried dumping $_FILES, $_POST, and $_REQUEST and all are empty. I assume that to be correct if the data is just being uploaded in a “raw” format?

I’ve been bashing my head with this for over a day now and am baffled. The thing that gets me is that it works for Parse, so what is it that I’m missing? Or do I just have deal with it manually via network.request() and base64 encode/decode in order to make it work for PHP? And is that any more costly than the standard network.upload()?

This really would be a useful thing to know, even on a stock Apache/PHP setup. Thanks in advance.

Cheers.