Andre
Andre

Reputation: 21

Sending empty <input type="file"> fields with Curl on PHP

Welcome everyone.

Here is a problem I have: I'm trying to send data with files to server using PHP and CURL. Server accepts data with 6 photos. And if there are less than 6 items in $_FILES array - it`s an error.

So if I send all 6 photos, everything goes perfectly. But if there are less photos, than value with empty photo goes to $_POST array.

I read Curl documentation but couldn`t find such possibility. How can it be done with CURL to send an empty value of File type?

Upvotes: 2

Views: 2927

Answers (4)

This is how you can always send a file input field even if it is empty:

<input type="text" name="team_logo" hidden>
<input type="file" name="team_logo">

I'm not familiar with curl, but this works in php laravel.

Upvotes: 0

Egorrishe
Egorrishe

Reputation: 48

I had the same problem. My PHP solution is to send data like browser does. Just try to use example 1:

Example 1

$postData = '------WebKitFormBoundarywPZdrW1CWvlHZ7xB
Content-Disposition: form-data; name="image[3]"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundarywPZdrW1CWvlHZ7xB--';
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);


Works fine? Now we need some function to build this data automatically. Example 2 is simple illustration how to do it using CurlHelper::prepareMultiPartData() function. You need to create class CurlHelper before (code listed below).

Example 2

$data=array( //array can be multidimensional like this
  'image' => array( 
    3 => curl_file_create('', '', ''),
  ),
);
$boundary = 'webkit';
$postData = CurlHelper::prepareMultiPartData($data, $boundary);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'url');//TODO set your own url
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: multipart/form-data; boundary=$boundary"));
$response = curl_exec($ch);

class CurlHelper

class CurlHelper {
  /**
   * @param array $data POST data. could be multidimensional array
   * @param string $boundary if $boundary == '' it will be generated automatically
   * @return string formatted POST data separeted with boundary like in browser.
   */
  static function prepareMultiPartData($data, &$boundary){
    $boundary = self::_createBoundary($boundary);
    $boundaryMiddle = "--$boundary\r\n";
    $boundaryLast = "--$boundary--\r\n";

    $res = self::_prepareMultipartData($data, ["\r\n"]);
    $res = join($boundaryMiddle, $res) . $boundaryLast;
    return $res;
  }

  static private function _createBoundary($boundaryCustom = '') {
    switch ($boundaryCustom) {
      case '':
        return uniqid('----').uniqid();
      case 'chrome':
      case 'webkit':
        return uniqid('----WebKitFormBoundary').'FxB';
      default:
        return $boundaryCustom;
    }
  }

  static private function _prepareMultipartData($data, $body, $keyTpl = ''){
    $ph = '{key}';
    if ( !$keyTpl ) {
      $keyTpl = $ph;
    }
    foreach ($data as $k => $v) {
      $paramName = str_replace($ph, $k, $keyTpl);
      if ( (class_exists('CURLFile') && $v instanceof \CURLFile) || strpos($v, '@') === 0 ) {
        if (is_string($v)) {
          $buf = strstr($v,'filename=');
          $buf = explode(';', $buf);
          $filename = str_replace('filename=', '', $buf[0]);
          $mimeType = (isset($buf[1])) ? str_replace('type=', '', $buf[1]) : 'application/octet-stream';
        } else {
          $filename = $v->name;
          $mimeType = $v->mime;
        }
        $str = 'Content-Disposition: form-data; name="' . $paramName . '"; filename="' . $filename . "\"\r\n";
        $str .= 'Content-Type: ' . $mimeType . "\r\n\r\n\r\n";
        $body[] = $str;
      } elseif ( is_array($v) ) {
        $body = self::_prepareMultipartData($v, $body, $paramName.'['.$ph.']');
      } else {
        $body[] = 'Content-Disposition: form-data; name="' . $paramName . "\"\r\n\r\n" . $v . "\r\n";
      }
    }
    return $body;
  }

} 

Upvotes: 0

Josef
Josef

Reputation: 1532

I had the same problem and this works:

curl  -F "[email protected]" -F "file_2=@/dev/null;filename=" -F "file_3=@/dev/null;filename="

Upvotes: 2

Pekka
Pekka

Reputation: 449783

I don't think this is possible: In a browser, an unused file input will simply not be attached to the request at all, so there is no "empty file field" value to parse.

$_FILES will be constructed by PHP when it receives the request carrying the uploaded files.

Not sure what you are trying to do here though - don't you already know the request will fail if you don't have six images to send in the first place?

Upvotes: 1

Related Questions