How to upload an image to iNaturalist using php cURL? - php

Have been working on posting observation photos to iNaturalist using iNaturalist POST/observation_photos api using php cURL but I am not able to do so. Did some research and found this on github, I tried the code but got error 'status':422.
My code:
<?php
require './init.php';
if ( isset($_POST['submitfile']) )
{
// Make sure there are no upload errors
if ($_FILES['upfile']['error'] > 0)
{
die("Error uploading file...");
}
$token = $_SESSION['access_token'];
$file_path = 'c:\Users\hp\Pictures\New folder';
$suffix = 'image/jpeg';
$photo_name = $_FILES['upfile']['name'];
$inat_id = 'White Magnolia';
$boundary = rand(10,100);
$body = '';
$body .= '--' . $boundary . "\r\n";
$body .= 'Content-Disposition: form-data; name="upfile"; filename=' . basename($file_path) . "\"\r\n";
$body .= 'Content-Type: ' . $suffix . "\r\n\r\n";
$body .=file_get_contents($photo_name) . "\r\n";
$body .= '--' . $boundary . "\r\n";
$body .= 'Content-Disposition: form-data; name="observation_photo[observation_id]"' . "\r\n";
$body .= 'Content-Type: application/json' . "\r\n\r\n";
$body .= json_encode(['observation_id' => $inat_id]) . "\r\n";
$body .= '--' . $boundary . '--' . "\r\n";
$photo_payload = array('observation_photos'=>array(
'method'=>'POST',
'timeout'=>'10',
'headers'=> array(
'Authorization'=>'Bearer'.$token,
'Content-Type'=>'multipart/form-data;'
),
'body' => $body
));
$payload = json_encode($photo_payload);
print_r($payload);
echo "<hr><br>";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.inaturalist.org/v1/observation_photos?access_token=".$token);
//curl_setopt($ch, CURLOPT_URL, "https://api.inaturalist.org/v1/observations?access_token=".$token);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:54.0) Gecko/20100101 Firefox/54.0");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$result = curl_exec($ch);
curl_close($ch);
// Print the result?
print_r($result);
}
?>
<hr>
<br>
<form action="" method="post" enctype="multipart/form-data">
Select file to upload:
<input type="file" name="upfile">
<input type="submit" value="Upload File" name="submitfile">
</form>
The error that I get while uploading image:

I spot 1 or 2 problems.
$file_path = realpath('white magnolia.jpg'); Shouldn't this be a file path (a location) rather than an actual file name?
Is it possible that your $photo_name('white magnolia.jpg') needs an underscore instead of a space? $photo_name('white_magnolia.jpg')
I'm almost certain item 1 above is your main problem, but I thought I'd point out the possibility that you might need an underscore just in case.

Related

Reg : how to send multiple files and other post params in CURL using php

I'm trying to post a request to a REST service using curl in PHP, but it is not working. Listed below is the piece of code, I have tried.
function curl_post_data($log,$url){
$clientid= 18;
$userid = 1614;
$voicesample1filename = realpath("RecordFiles/utterance1.wav");
$voicesample2filename = realpath("RecordFiles/utterance2.wav");
$voicesample3filename = realpath("RecordFiles/utterance3.wav");
$payload['sessionid'] = '13738B27-C664-57B0-2ABF-8873914E971A';
$payload['clienid'] = $clientid;
$payload['userid'] = $userid;
$payload['type'] = 8;
$payload['action'] = 'registration';
// build multipart
$payload = http_build_query($payload);
if ( is_file($voicesample1filename) ) {
$log->LogInfo("File '" . $voicesample1filename . "' exists");
} else {
$log->LogInfo("File '" . $voicesample1filename . "' does not exists");
}
$params = "--ABC1234\r\n"
. "Content-Type: application/x-www-form-urlencoded\r\n"
. "\r\n"
. $payload . "\r\n"
. "--ABC1234\r\n"
. "Content-Type: audio/wav\r\n"
. "Content-Disposition: attachment; utterance1=\"attachment1.wav\"\r\n"
. "\r\n"
. file_get_contents($voicesample1filename) . "\r\n"
. "--ABC1234\r\n"
. "Content-Type: audio/wav\r\n"
. "Content-Disposition: attachment; utterance2=\"attachment2.wav\"\r\n"
. "\r\n"
. file_get_contents($voicesample2filename) . "\r\n"
. "--ABC1234\r\n"
. "Content-Type: audio/wav\r\n"
. "Content-Disposition: attachment; utterance3=\"attachment3.wav\"\r\n"
. "\r\n"
. file_get_contents($voicesample3filename) . "\r\n"
. "--ABC1234--";
$log->LogInfo("Request is :" .$params);
$first_newline = strpos($params, "\r\n");
$multipart_boundary = substr($params, 2, $first_newline - 2);
$request_headers = array();
$request_headers[] = 'Content-Length: ' . strlen($params);
$request_headers[] = 'Content-Type: multipart/form-data; boundary='. $multipart_boundary;
$log->LogInfo("PayLoad Array is :" .print_r($payload,true));
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
//curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
$reply = curl_exec($ch);
$arrayOfResult=json_decode($reply,true);
}
If I execute the code, I'm getting multipart request error.
You don't need to use the 'Content-Disposition' things manually. Curl does it automatically when it finds a file to send. You can consider the following example for this case. curl will adjust the required multipart headers if you use something similar with your code.
$params = array("name" => "some-name",
"id" => "1231",
"file1" => "#c:/temp/file1.wav",
"file2" => "#c:/temp/file2.wav"
);

Post a file via Request Factory in Kohana

Is it possible to simulate a file upload request in Kohana 3.2? I was trying the following but not having much luck:
$file = file_get_contents('../../testimage.jpg');
$request = new Request('files');
$request->method(HTTP_Request::POST);
$request->post('myfile', $file);
//$request->body($file);
$request->headers(array(
'content-type' => 'multipart/mixed;',
'content-length' => strlen($file)
));
$request->execute();
This Kohana forum post indicates that it should be possible. Given the similarity to your code, I'm guessing you already found that. As that's not working for you, you could try cURL:
$postData = array('myfile' => '#../../testimage.jpg');
$uri = 'files';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_POST, true);
$response = curl_exec($ch);
If you want to use the Kohana Request, you could try building your own multipart body using this code (I don't have the proper setup to test it right now, but it should be close to what you need):
$boundary = '---------------------' . substr(md5(rand(0,32000)), 0, 10);
$contentType = 'multipart/form-data; boundary=' . $boundary;
$eol = "\r\n";
$contents = file_get_contents('../../testimage.jpg');
$bodyData = '--' . $boundary . $eol;
$bodyData .= 'Content-Type: image/jpeg' . $eol;
$bodyData .= 'Content-Disposition: form-data; name="myfile"; filename="testimage.jpg"' . $eol;
$bodyData .= 'Content-Transfer-Encoding: binary' . $eol;
$bodyData .= $contents . $eol;
$bodyData .= '--' . $boundary . '--' . $eol . $eol;
$request = new Request('files');
$request->method(HTTP_Request::POST);
$request->headers(array('Content-Type' => $contentType));
$request->body($data);
$request->execute();
Found a pull request from GitHub that discusses the matter. I ended up adding some testing-code to my controller to get around the problem:
if ($this->request->query('unittest'))
{
// For testing, don't know how to create internal requests with files attached.
// #link http://stackoverflow.com/questions/10988622/post-a-file-via-request-factory-in-kohana
$raw_file = file_get_contents(APPPATH.'tests/test_data/sample.txt');
}
A Request::files() method would be nice tho.

Multipart PUT upload using Curl and PHP to a REST endpoint

I need to HTTP PUT a csv file and some POST fields using multipart POST with PHP and Curl to a REST API endpoint.
The contents of the file upload is stored in a variable $list. The other end point is $url.
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true);
$post = array(
//Other Post fields array
);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$fh = fopen('php://memory', 'rw');
fwrite($fh, $list);
rewind($fh);
curl_setopt($ch, CURLOPT_INFILE, $fh);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($list));
$response = curl_exec($ch);
The above code seems to work the only problem is that the other end point requires a specific fieldname for the file upload. How do i set a filename ?
Am i doing something wrong ?
This is the PUT format they have mentioned on API
Content-Disposition: form-data; name="list[csv]"; filename="RackMultipart20110923-63966-hfpyg"
Content-Length: 33
Content-Type: text/csv
Content-Transfer-Encoding: binary
xxxx
yyyy
zzzz
-------------MultipartPost
Content-Disposition: form-data; name="list[list_type]"
Blacklist
-------------MultipartPost--
FYI that is multipart/form-data. You will need to build the body yourself I think, I don't think cURL could be made to build that sort of request with a PUT request. However, this is not a serious problem:
<?php
function recursive_array_mpfd ($array, $separator, &$output, $prefix = '') {
// Recurses through a multidimensional array and populates $output with a
// multipart/form-data string representing the data
foreach ($array as $key => $val) {
$name = ($prefix) ? $prefix."[".$key."]" : $key;
if (is_array($val)) {
recursive_array_mpfd($val, $separator, $output, $name);
} else {
$output .= "--$separator\r\n"
. "Content-Disposition: form-data; name=\"$name\"\r\n"
. "\r\n"
. "$val\r\n";
}
}
}
// This will hold the request body string
$requestBody = '';
// We'll need a separator
$separator = '-----'.md5(microtime()).'-----';
// First add the postfields
$post = array(
//Other Post fields array
);
recursive_array_mpfd($post, $separator, $requestBody);
// Now add the file
$list = "this,is,some,csv,data"; // The content of the file
$filename = "data.csv"; // The name of the file
$requestBody .= "--$separator\r\n"
. "Content-Disposition: form-data; name=\"list[list_type]\"; filename=\"$filename\"\r\n"
. "Content-Length: ".strlen($list)."\r\n"
. "Content-Type: text/csv\r\n"
. "Content-Transfer-Encoding: binary\r\n"
. "\r\n"
. "$list\r\n";
// Terminate the body
$requestBody .= "--$separator--";
// Let's go cURLing...
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: multipart/form-data; boundary="'.$separator.'"'
));
$response = curl_exec($ch);
If you have any problems with this, try echo $requestBody; before the cURL request and make sure it looks like you expect it to.

CURL - php vs command line bash

From bash terminal, I successfully executed the following command.
curl -v -L -F file='#/var/www/dev/public_html/sixties.mov' -F title='my video' -F description='this is a video' -F language='eng' -F license='a2be14e1-37d9-11dd-ae16-0800200c9a66' -F country='US' http://johnuser:johnpass#website.com/api/media
Now I want to create a PHP script that uses the phpcurl library to execute an equivalent command. My code below is shown, but it's not working. The http://johnuser:johnpass#website.com/api/media server is giving me a generic error message. I'm pretty sure I'm making a mistake by not passing the right parameters or setting the right flags in my php code. Can anyone tell me what's wrong?
$url = 'http://johnuser:johnpass#website.com/api/media';
$fields = array();
$fields['file'] = '#/var/www/dev/public_html/sixties.mov';
$fields['title'] = 'my video';
$fields['description'] = 'this is a test';
$fields['language'] = 'eng';
$fields['country'] = 'US';
$fields['license'] = 'a2be14e1-37d9-11dd-ae16-0800200c9a66';
foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
rtrim($fields_string,'&');
$ch = curl_init();
//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POST,count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
//execute post
$result = curl_exec($ch);
//close connection
curl_close($ch);
print_r($result);
The error message I got is {"status":{"message":"typeMismatch ","error":true,"code":500}}
I think this is what you need to be doing, if you want upload a file:
// Parameters
$url = 'http://website.com/api/media';
$username = 'johnuser';
$password = 'johnpass';
$upload_file = '/var/www/dev/public_html/sixties.mov';
// Declare a couple of arrays we will need
$fields = $headers = array();
// Standard POST fields
$fields['title'] = 'my video';
$fields['description'] = 'this is a test';
$fields['language'] = 'eng';
$fields['country'] = 'US';
$fields['license'] = 'a2be14e1-37d9-11dd-ae16-0800200c9a66';
// Boundary string for multipart message
$boundary = '--=-=-'.md5(uniqid()).rand().'-=-=--';
// Start the body with the file to be uploaded
$body = "--$boundary\r\n"
. "Content-Disposition: form-data; name=\"file\"; filename=\"".basename($upload_file)."\"\r\n"
. "Content-Type: application/octet-stream\r\n" // You should put the right MIME type here
. "\r\n"
. file_get_contents($upload_file) . "\r\n";
// Loop the fields and build the rest of the body
foreach ($fields as $name => $value) {
$body .= "--$boundary\r\n"
. "Content-Disposition: form-data; name=\"$name\"\r\n"
. "\r\n"
. "$value\r\n";
}
// Finish the body
$body .= "--$boundary--";
// Add a couple of headers
$headers[] = "Content-Type: multipart/form-data; boundary=\"$boundary\"";
$headers[] = 'Content-Length: ' . strlen($body);
$ch = curl_init();
// Set the cURL options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
// Execute post
$result = curl_exec($ch);
// Close connection
curl_close($ch);
print_r($result);
POST file uploads are done with a MIME multipart message, using the multipart/form-data sub-type.

Post CSV to Google Docs Folder

Working on a PHP script to essentially dump the results of a query into a .csv and then upload to a specific folder on our enterprise Google Docs.
The file is created and sent over to Docs just fine. However, it isn't being placed in the proper directory.
This is the function I'm using where $file, $folder, and $newtoken are strings like 'dump.csv', '0B0rVKeOmIwGXMGU2MzYyN2EtZWV...', [auth token from Google].
function insert($file, $folder, $newtoken){
$mime = "Media multipart posting\n--END_OF_PART\n";
$mime .= "Content-Type: application/atom+xml\n\n";
$xml="<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:docs=\"http://schemas.google.com/docs/2007\">
<category scheme=\"http://schemas.google.com/g/2005#kind\"
term=\"http://schemas.google.com/docs/2007#document\"/>
<title>example document</title>
<docs:writersCanInvite value=\"true\" />
</entry>
";
$mime.=$xml."\n--END_OF_PART\n";
$document = "Content-Type: text/plain\n\n";
$handle = fopen($file, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
$document.=$contents;
$itemURL = "https://docs.google.com/feeds/default/private/full";
$mime .= $document."\n--END_OF_PART--\n";
$headers = array();
$headers[] = "POST /feeds/default/private/full/folder%3A".$folder."/contents HTTP/1.1";
$headers[] = "Host: docs.google.com";
$headers[] = "GData-Version: 3.0";
$headers[] = "Authorization: GoogleLogin auth=".$newtoken."";
$headers[] = "Content-Length: ".strlen($mime);
$headers[] = "Content-Type: multipart/related; boundary=END_OF_PART";
$headers[] = "Slug: Test";
$curl = curl_init();
curl_setopt($curl,CURLOPT_URL,$itemURL);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl,CURLOPT_POSTFIELDS, $mime);
$result = curl_exec($curl);
curl_close($curl);
echo 'Response: '.$result;
}
Maybe I have the folder ID wrong (just copied from URL while viewing in Google Docs)?
Bonus: Can I manipulate the content-type headers to get Google to convert the file to a spreadsheet?
You have
$headers[] = "POST /feeds/default/private/full/folder%3A".$folder."/contents HTTP/1.1";
and then:
curl_setopt($curl,CURLOPT_URL,$itemURL);
Make up your mind. I think the first one is wrong; you can't change the URL via the CURLOPT_HTTPHEADER option. Set $itemURL to the full URL (including /feeds/default/private/full/folder%3A".$folder."/contents) and remove the first line.

Categories