Search code examples
phpcsvpostcurlfputcsv

How to programatically HTTP POST a CSV file directly from an array in PHP?


I'm contributing data to the opencellid.org website. They have several API options to submit data but the only bulk method requires an HTTP POST file upload with the API key as a POST field. Formats acceptable are CSV, JSON and CLF3. I store the data in an SQL database for internal use and periodically submit data to opencellid.

At the moment the script that I use to submit the data to opencellid queries the SQL DB for the most recent measurements and then saves it as a CSV file on the server and then immediately uploads it via HTTP POST. In my eyes this is inelegant.

So my question is, can you POST upload a CSV file directly from an array without first having to actually create a CSV file on the server?

Here's the code snippet we currently use.

//Create and save CSV
$output = fopen("opencellid.csv","w") or die();
fputcsv($output, array_keys($celldata[0]));

foreach($celldata as $cell) {
    fputcsv($output, $cell);
}   

fclose($output) or die();

//Upload CSV
$target_url = 'http://opencellid.org/measure/uploadCsv';

$file_name_with_full_path = realpath('./opencellid.csv');

$post = array('key' => 'opencellidapikey',
              'datafile'=>'@'.$file_name_with_full_path.";type=text/plain");

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$target_url);
curl_setopt($ch, CURLOPT_POST,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);

$postresult = curl_exec ($ch);

Can anyone suggest a way to directly upload a CSV from an array?


Solution

  • I managed to capture the csv data in to a variable using fputcsv to php://output with output buffering. Since the service only allows multipart format for the submission, you need to construct the payload like this.

    <?php
    
    //Construct your csv data
    $celldata = array(
        array(
            "heading1",
            "heading2",
            "heading3",
        ),
        array(
            1,
            2,
            3,
        ),
        array(
            4,
            5,
            6,
        )
    );
    
    //Output to php://output
    ob_start();
    $outstream = fopen("php://output", 'w');
    foreach($celldata as $cell) {
        fputcsv($outstream, $cell);
    }   
    fclose($outstream) or die();
    $csv_data = ob_get_clean();
    
    $url = 'http://opencellid.org/measure/uploadCsv';
    // Taken from http://stackoverflow.com/questions/3085990/post-a-file-string-using-curl-in-php
    
    // form field separator
    $delimiter = '-------------' . uniqid();
    // file upload fields: name => array(type=>'mime/type',content=>'raw data')
    $fileFields = array(
        'datafile' => array(
            'type' => 'text/plain',
            'content' => $csv_data,
        ),
    );
    // all other fields (not file upload): name => value
    $postFields = array(
        'key'   => 'opencellidapikey', //Put your api key here
    );
    
    $data = '';
    
    // populate normal fields first (simpler)
    foreach ($postFields as $name => $content) {
        $data .= "--" . $delimiter . "\r\n";
        $data .= 'Content-Disposition: form-data; name="' . $name . '"';
        $data .= "\r\n\r\n";
        $data .= $content;
        $data .= "\r\n";
    }
    // populate file fields
    foreach ($fileFields as $name => $file) {
        $data .= "--" . $delimiter . "\r\n";
        $data .= 'Content-Disposition: form-data; name="' . $name . '";' .
                 ' filename="' . $name . '"' . "\r\n";
        $data .= 'Content-Type: ' . $file['type'] . "\r\n";
        $data .= "\r\n";
        $data .= $file['content'] . "\r\n";
    }
    $data .= "--" . $delimiter . "--\r\n";
    
    $handle = curl_init($url);
    curl_setopt($handle, CURLOPT_POST, true);
    curl_setopt($handle, CURLOPT_HTTPHEADER , array(
        'Content-Type: multipart/form-data; boundary=' . $delimiter,
        'Content-Length: ' . strlen($data)));  
    curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
    curl_setopt($handle, CURLOPT_RETURNTRANSFER,1);
    $result = curl_exec($handle);
    var_dump($result);
    

    I get API key error, but it should work.