Usar curl para cargar datos POST con archivos

415

Me gustaría usar cURL no solo para enviar parámetros de datos en HTTP POST sino también para cargar archivos con un nombre de formulario específico. ¿Cómo debo hacer eso?

Parámetros de publicación HTTP:

userid = 12345 filecomment = Este es un archivo de imagen

Carga del archivo HTTP: Ubicación del archivo = /home/user1/Desktop/test.jpg Nombre del formulario para el archivo = imagen (corresponde a $ _FILES ['imagen'] en el lado de PHP)

Imaginé parte del comando cURL de la siguiente manera:

curl -d "userid=1&filecomment=This is an image file" --data-binary @"/home/user1/Desktop/test.jpg" localhost/uploader.php

El problema que tengo es el siguiente:

Notice: Undefined index: image in /var/www/uploader.php

El problema es que estoy usando $ _FILES ['imagen'] para recoger archivos en el script PHP.

¿Cómo ajusto mis comandos cURL en consecuencia?

thotheolh
fuente

Respuestas:

657

Necesitas usar la -Fopción:
-F/--form <name=content> Specify HTTP multipart POST data (H)

Prueba esto:

curl \
  -F "userid=1" \
  -F "filecomment=This is an image file" \
  -F "image=@/home/user1/Desktop/test.jpg" \
  localhost/uploader.php
pobre
fuente
1
Estoy confundido por la parte sobre la codificación de URL del archivo. He subido archivos JPG y PNG como este sin modificarlos, sin ningún problema.
Deanna Gelbart
1
@DavidGelbart Tienes razón. Mi respuesta inicial hizo referencia a la -dopción por error, que necesita la entrada codificada en URL. Debería haberlo eliminado cuando actualicé la respuesta a la -Fopción. Gracias por atrapar eso.
jimp
3
@ user956424 En el ejemplo, establezca "imagen" con el nombre de su campo. Y algunos lenguajes, como PHP, crearán una matriz si especifica algo como "imagen []" para las entradas que deben agruparse.
jimp
1
¿Qué es el @en image=@/..?
Timo
2
@Timo Significa que el contenido del campo de formulario nombrado debe cargarse desde una ruta de archivo. Sin él, se pasa el argumento de cadena en sí.
jimp
94

Capturando la identificación de usuario como variable de ruta (recomendado):

curl -i -X POST -H "Content-Type: multipart/form-data" 
-F "[email protected]" http://mysuperserver/media/1234/upload/

Capturando la identificación de usuario como parte del formulario:

curl -i -X POST -H "Content-Type: multipart/form-data" 
-F "[email protected];userid=1234" http://mysuperserver/media/upload/

o:

curl -i -X POST -H "Content-Type: multipart/form-data" 
-F "[email protected]" -F "userid=1234" http://mysuperserver/media/upload/
r1ckr
fuente
16
use -F no necesita establecer"Content-Type: multipart/form-data"
William Hu
10
No pude hacer que -F funcione correctamente con ese separador de punto y coma que indicaste. En cambio, tuve que proporcionar dos argumentos -F redundantes. Me gusta: -F "[email protected]" -F "userid = 1234"
robbpriestley
22

Aquí está mi solución, he estado leyendo muchas publicaciones y fueron realmente útiles. Finalmente escribí un código para archivos pequeños, con cURL y PHP que creo que es realmente útil.

public function postFile()
{    
        $file_url = "test.txt";  //here is the file route, in this case is on same directory but you can set URL too like "http://examplewebsite.com/test.txt"
        $eol = "\r\n"; //default line-break for mime type
        $BOUNDARY = md5(time()); //random boundaryid, is a separator for each param on my post curl function
        $BODY=""; //init my curl body
        $BODY.= '--'.$BOUNDARY. $eol; //start param header
        $BODY .= 'Content-Disposition: form-data; name="sometext"' . $eol . $eol; // last Content with 2 $eol, in this case is only 1 content.
        $BODY .= "Some Data" . $eol;//param data in this case is a simple post data and 1 $eol for the end of the data
        $BODY.= '--'.$BOUNDARY. $eol; // start 2nd param,
        $BODY.= 'Content-Disposition: form-data; name="somefile"; filename="test.txt"'. $eol ; //first Content data for post file, remember you only put 1 when you are going to add more Contents, and 2 on the last, to close the Content Instance
        $BODY.= 'Content-Type: application/octet-stream' . $eol; //Same before row
        $BODY.= 'Content-Transfer-Encoding: base64' . $eol . $eol; // we put the last Content and 2 $eol,
        $BODY.= chunk_split(base64_encode(file_get_contents($file_url))) . $eol; // we write the Base64 File Content and the $eol to finish the data,
        $BODY.= '--'.$BOUNDARY .'--' . $eol. $eol; // we close the param and the post width "--" and 2 $eol at the end of our boundary header.



        $ch = curl_init(); //init curl
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                         'X_PARAM_TOKEN : 71e2cb8b-42b7-4bf0-b2e8-53fbd2f578f9' //custom header for my api validation you can get it from $_SERVER["HTTP_X_PARAM_TOKEN"] variable
                         ,"Content-Type: multipart/form-data; boundary=".$BOUNDARY) //setting our mime type for make it work on $_FILE variable
                    );
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/1.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0'); //setting our user agent
        curl_setopt($ch, CURLOPT_URL, "api.endpoint.post"); //setting our api post url
        curl_setopt($ch, CURLOPT_COOKIEJAR, $BOUNDARY.'.txt'); //saving cookies just in case we want
        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); // call return content
        curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1); navigate the endpoint
        curl_setopt($ch, CURLOPT_POST, true); //set as post
        curl_setopt($ch, CURLOPT_POSTFIELDS, $BODY); // set our $BODY 


        $response = curl_exec($ch); // start curl navigation

     print_r($response); //print response

}

Con esto deberíamos estar en el "api.endpoint.post" que publican los siguientes vars. Puede probar fácilmente con este script, y debería recibir estos debugs en la función postFile()en la última fila.

print_r($response); //print response

public function getPostFile()
{

    echo "\n\n_SERVER\n";
    echo "<pre>";
    print_r($_SERVER['HTTP_X_PARAM_TOKEN']);
    echo "/<pre>";
    echo "_POST\n";
    echo "<pre>";
    print_r($_POST['sometext']);
    echo "/<pre>";
    echo "_FILES\n";
    echo "<pre>";
    print_r($_FILEST['somefile']);
    echo "/<pre>";
}

Debería funcionar bien, pueden ser mejores soluciones, pero esto funciona y es realmente útil para comprender cómo funciona el límite y el mimo multiparte / desde datos en PHP y la biblioteca cURL.

Libertese
fuente
si necesita enviar un archivo no codificado, cambie estas líneas $ BODY. = 'Content-Transfer-Encoding: multipart / form-data'. $ eol. $ eol; // colocamos el último Contenido y 2 $ eol, $ BODY. = file_get_contents ($ file_url). $ eol; // escribimos el Contenido del archivo Base64 y el $ eol para finalizar los datos,
andreah
8

si está cargando un archivo binario como csv, use el formato siguiente para cargar el archivo

curl -X POST \
    'http://localhost:8080/workers' \
    -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyIsInR5cGUiOiJhY2Nlc3MifQ.eyJ1c2VySWQiOjEsImFjY291bnRJZCI6MSwiaWF0IjoxNTExMzMwMzg5LCJleHAiOjE1MTM5MjIzODksImF1ZCI6Imh0dHBzOi8veW91cmRvbWFpbi5jb20iLCJpc3MiOiJmZWF0aGVycyIsInN1YiI6ImFub255bW91cyJ9.HWk7qJ0uK6SEi8qSeeB6-TGslDlZOTpG51U6kVi8nYc' \
    -H 'content-type: application/x-www-form-urlencoded' \
    --data-binary '@/home/limitless/Downloads/iRoute Masters - Workers.csv'
KARTHIKEYAN.A
fuente
44
Me gustaría ver un ejemplo de un archivo csv binario.
polis
44
@polis, la opción --data-binaryindica curlque no se realice ningún procesamiento previo de los datos, en lugar de --datamarcar. para abordar su comentario directamente, tenga en cuenta que el texto también es binario, pero podemos interpretarlo como caracteres ASCII. Si realmente quieres un ejemplo distinto, piensa en un CSV cuyos campos contengan emoji. Sus bytes no se asignan directamente al texto
Ciprian Tomoiagă
3

El problema que me llevó aquí resultó ser un error básico del usuario: no estaba incluyendo el @signo en la ruta del archivo y curl estaba publicando la ruta / nombre del archivo en lugar de los contenidos. Por Content-Lengthlo tanto, el valor era 8 en lugar de los 479 que esperaba ver dada la legibilidad de mi archivo de prueba.

El Content-Lengthencabezado se calculará automáticamente cuando curl lea y publique el archivo.

curl -i -H "Content-Type: application/xml" --data "@test.xml" -v -X POST https://<url>/<uri/

... <Contenido-Longitud: 479 ...

Publicar esto aquí para ayudar a otros novatos en el futuro.

usuario shonky de linux
fuente
2

Como alternativa curl, puede usar HTTPie , es una CLI, una herramienta similar a cURL para humanos.

  1. Instrucciones de instalación: https://github.com/jakubroztocil/httpie#installation

  2. Entonces corre:

    http -f POST http://localhost:4040/api/users username=johnsnow photo@images/avatar.jpg
    
    HTTP/1.1 200 OK
    Access-Control-Expose-Headers: X-Frontend
    Cache-control: no-store
    Connection: keep-alive
    Content-Encoding: gzip
    Content-Length: 89
    Content-Type: text/html; charset=windows-1251
    Date: Tue, 26 Jun 2018 11:11:55 GMT
    Pragma: no-cache
    Server: Apache
    Vary: Accept-Encoding
    X-Frontend: front623311
    
    ...
Gianfranco P.
fuente
2

Después de muchos intentos, este comando funcionó para mí:

curl -v -F filename=image.jpg -F upload=@image.jpg http://localhost:8080/api/upload
Evandro Pomatti
fuente
1

Aquí se explica cómo escapar correctamente de los nombres de archivos arbitrarios de los archivos cargados con bash:

#!/bin/bash
set -eu

f="$1"
f=${f//\\/\\\\}
f=${f//\"/\\\"}
f=${f//;/\\;}

curl --silent --form "uploaded=@\"$f\"" "$2"
Vladimir Panteleev
fuente
0

Lo hice funcionar con este comando curl -F 'filename=@/home/yourhomedirextory/file.txt' http://yourserver/upload

Shraavan Hebbar
fuente