Utilisation de curl pour télécharger des données POST avec des fichiers

415

Je voudrais utiliser cURL pour envoyer non seulement des paramètres de données dans HTTP POST mais aussi pour télécharger des fichiers avec un nom de formulaire spécifique. Comment dois-je procéder?

Paramètres de publication HTTP:

userid = 12345 filecomment = Ceci est un fichier image

Téléchargement de fichier HTTP: Emplacement du fichier = /home/user1/Desktop/test.jpg Nom du formulaire pour file = image (correspond à $ _FILES ['image'] du côté PHP)

J'ai figuré une partie de la commande cURL comme suit:

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

Le problème que je rencontre est le suivant:

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

Le problème est que j'utilise $ _FILES ['image'] pour récupérer des fichiers dans le script PHP.

Comment ajuster mes commandes cURL en conséquence?

thotheolh
la source

Réponses:

657

Vous devez utiliser l' -Foption:
-F/--form <name=content> Specify HTTP multipart POST data (H)

Essaye ça:

curl \
  -F "userid=1" \
  -F "filecomment=This is an image file" \
  -F "image=@/home/user1/Desktop/test.jpg" \
  localhost/uploader.php
jimp
la source
1
Je suis confus par la partie sur le codage url du fichier. J'ai téléchargé des fichiers JPG et PNG comme celui-ci sans les modifier, sans aucun problème.
Deanna Gelbart
1
@DavidGelbart Vous avez raison. Ma réponse initiale faisait référence à l' -doption par erreur, qui a besoin de l'URL d'entrée encodée. J'aurais dû supprimer cela lorsque j'ai mis à jour la réponse à l' -Foption. Merci d'avoir attrapé ça.
jimp
3
@ user956424 Dans l'exemple, définissez "image" sur le nom de votre champ. Et certains langages, comme PHP, construiront un tableau si vous spécifiez quelque chose comme "image []" pour les entrées qui doivent être regroupées.
jimp
1
Quel est le @dans image=@/..?
Timo
2
@Timo Cela signifie que le contenu du champ de formulaire nommé doit être chargé à partir d'un chemin de fichier. Sans lui, l'argument chaîne lui-même est transmis.
jimp
94

Attraper l'ID utilisateur en tant que variable de chemin (recommandé):

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

Attraper l'ID utilisateur dans le cadre du formulaire:

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

ou:

curl -i -X POST -H "Content-Type: multipart/form-data" 
-F "[email protected]" -F "userid=1234" http://mysuperserver/media/upload/
r1ckr
la source
16
use -F need not set"Content-Type: multipart/form-data"
William Hu
10
Je n'ai pas pu faire fonctionner correctement -F avec le séparateur point-virgule que vous avez indiqué. Au lieu de cela, j'ai dû fournir deux arguments -F redondants. Comme: -F "[email protected]" -F "userid = 1234"
robbpriestley
22

Voici ma solution, j'ai lu beaucoup de messages et ils ont été vraiment utiles. Enfin, j'ai écrit du code pour les petits fichiers, avec cURL et PHP que je pense que c'est vraiment utile.

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

}

Avec cela, nous devrions être sur le "api.endpoint.post" les vars suivants postés. Vous pouvez facilement tester avec ce script, et vous devriez recevoir ce débogage sur la fonction postFile()à la dernière ligne.

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>";
}

Cela devrait bien fonctionner, ils peuvent être de meilleures solutions, mais cela fonctionne et est vraiment utile pour comprendre comment fonctionne le mime Boundary et multipart / from-data sur PHP et la bibliothèque cURL.

Libertese
la source
si vous devez envoyer un fichier non codé, modifiez ces lignes $ BODY. = 'Content-Transfer-Encoding: multipart / form-data'. $ eol. $ eol; // nous mettons le dernier contenu et 2 $ eol, $ BODY. = file_get_contents ($ file_url). $ eol; // nous écrivons le contenu du fichier Base64 et $ eol pour terminer les données,
andreah
8

si vous téléchargez un fichier binaire tel que csv, utilisez le format ci-dessous pour télécharger le fichier

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
la source
4
Je voudrais voir un exemple de fichier csv binaire.
polis
4
@polis l'option --data-binaryindique curlde ne pas effectuer de prétraitement des données, par opposition à --dataflag. pour adresser votre commentaire directement, notez que le texte est également binaire, mais nous pouvons l'interpréter comme des caractères ASCII. Si vous voulez vraiment un exemple distinct, pensez à un CSV dont les champs contiennent des emoji. Leurs octets ne correspondent pas directement au texte
Ciprian Tomoiagă
3

Le problème qui m'a conduit ici s'est avéré être une erreur utilisateur de base - je n'incluais pas le @signe dans le chemin du fichier et donc curl publiait le chemin / nom du fichier plutôt que le contenu. La Content-Lengthvaleur était donc de 8 plutôt que du 479 que je m'attendais à voir étant donné la longueur de mon fichier de test.

L'en- Content-Lengthtête sera automatiquement calculé lorsque curl lit et publie le fichier.

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

... <Longueur du contenu: 479 ...

L'afficher ici pour aider d'autres débutants à l'avenir.

utilisateur shonky linux
la source
2

Comme alternative à curl, vous pouvez utiliser HTTPie , c'est une CLI, un outil semblable à cURL pour les humains.

  1. Instructions d'installation: https://github.com/jakubroztocil/httpie#installation

  2. Ensuite, exécutez:

    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.
la source
2

Après beaucoup d'essais, cette commande a fonctionné pour moi:

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

Voici comment échapper correctement les noms de fichiers arbitraires des fichiers téléchargés avec bash:

#!/bin/bash
set -eu

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

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

Je l'ai fait fonctionner avec cette commande curl -F 'filename=@/home/yourhomedirextory/file.txt' http://yourserver/upload

Shraavan Hebbar
la source