I'm developing a WordPress website that allows users to submit observations that are then pushed to the iNaturalist API. I have successfully authenticated, pushed the observation data, and received the ID of the observation from the API. I am completely hung up on pushing a photo with the POST /observation_photos endpoint. My suspicion is that I'm not formatting the payload correctly, but I can't find any working examples of this type of request in a language I'm familiar with, so there's a lot of blind stabbing in the dark.
I've tried a lot of things before posting here so bear with me as I go through them and the results of each.
First, I tried the following:
$photo_payload = [
'method' => 'POST',
'timeout' => 10,
'headers' => [
'Authorization' => "Bearer $auth",
'Content-Type' => "multipart/form-data;"
],
'body' => [
'observation_photo' => [
'observation_id' => $inat_id
],
'file' =>file_get_contents($photo_sized)
]
];
$post_photo = wp_remote_post($inat_base_url . '/observation_photos', $photo_payload);
Which results in: 500 Internal Server Error
Then I tried removing the file from the body array and just send:
$photo_payload = [
'method' => 'POST',
'timeout' => 10,
'headers' => [
'Authorization' => "Bearer $auth",
'Content-Type' => "multipart/form-data;"
],
'body' => [
'observation_photo' => [
'observation_id' => $inat_id
],
'file' => ''
]
];
We get a success and iNaturalist adds a placeholder "Processing..." image to the observation (as seen here: https://www.inaturalist.org/observations/8014211).
So that tells me that the file data is not being sent properly. I tried base64_encoding the image data but got a 500 error for that too.
Next up was this attempt:
$photo_payload = [
'method' => 'POST',
'timeout' => 10,
'headers' => [
'Authorization' => "Bearer $auth",
'Content-Type' => "multipart/form-data;"
],
'body' => [
'observation_photo' => [
'observation_id' => $inat_id
],
'file' => '@' . $photo_sized . ';filename=' . basename($photo_sized) . ';type=' . get_post_mime_type($photo_id)
]
];
That gave me another 500 Internal Server Error
.
Then I started down the multipart/form-data rabbit hole and based on this post (http://codechutney.com/posting-file-using-wp_remote_post/) tried:
$boundary = wp_generate_password( 24 );
$body = '';
$body .= '--' . $boundary . "
";
$body .= 'Content-Disposition: form-data; name="file"; filename="' . basename($photo_sized) . "\"
";
$body .= 'Content-Type: ' . $photo_type . "
";
$body .= base64_encode(file_get_contents($photo_sized)) . "
";
$body .= '--' . $boundary . "
";
$body .= 'Content-Disposition: form-data; name="observation_photo"' . "
";
$body .= 'Content-Type: application/json' . "
";
$body .= json_encode(['observation_id' => $inat_id]) . "
";
$body .= '--' . $boundary . '--' . "
";
$photo_payload = [
'method' => 'POST',
'timeout' => 10,
'headers' => [
'Authorization' => "Bearer $auth",
'Content-Type' => "multipart/form-data; boundary=$boundary"
],
'body' => $body
];
Alas, another 500 Internal Server Error
.
Seriously, I have been banging my head against the wall on this for so long that I can't remember all the things I tried. At some point, I know I was getting timeout errors too, but I can't recreate them right now.
Other notes: It doesn't seem to change anything if I change the endpoint from observation_photos to observation_photos.json. The file that I am trying to send is 7585 bytes.
Heard back from a developer at iNaturalist! They let me know the proper formatting of the multipart request is as follows:
[body] => --abc
Content-Disposition: form-data; name="file"; filename="20170816_071116-150x150.jpg"
Content-Type: image/jpeg
[raw image data]
--abc
Content-Disposition: form-data; name="observation_photo[observation_id]"
8014211
--abc--
So I changed the body of the payload to the following and it works!
$body = '';
$body .= '--' . $boundary . "
";
$body .= 'Content-Disposition: form-data; name="file"; filename="' . basename($photo_sized) . "\"
";
$body .= 'Content-Type: ' . $photo_type . "
";
$body .= base64_encode(file_get_contents($photo_sized)) . "
";
$body .= '--' . $boundary . "
";
$body .= 'Content-Disposition: form-data; name="observation_photo[observation_id]"' . "
";
$body .= 'Content-Type: application/json' . "
";
$body .= $inat_id . "
";
$body .= '--' . $boundary . '--' . "
";
Here's a link to the solution on GitHub issues: https://github.com/inaturalist/inaturalist/issues/1483#issuecomment-331239325