Usando curl para fazer upload de dados POST com arquivos

414

Gostaria de usar o cURL não apenas para enviar parâmetros de dados no HTTP POST, mas também para enviar arquivos com nome de formulário específico. Como devo fazer isso?

Parâmetros de postagem HTTP:

userid = 12345 filecomment = Este é um arquivo de imagem

Upload de arquivo HTTP: local do arquivo = /home/user1/Desktop/test.jpg Nome do formulário para file = image (corresponde a $ _FILES ['image'] no lado do PHP)

Imaginei parte do comando cURL da seguinte maneira:

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

O problema que estou recebendo é o seguinte:

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

O problema é que estou usando $ _FILES ['image'] para pegar arquivos no script PHP.

Como ajusto meus comandos cURL de acordo?

thotheolh
fonte

Respostas:

656

Você precisa usar a -Fopção:
-F/--form <name=content> Specify HTTP multipart POST data (H)

Tente o seguinte:

curl \
  -F "userid=1" \
  -F "filecomment=This is an image file" \
  -F "image=@/home/user1/Desktop/test.jpg" \
  localhost/uploader.php
jimp
fonte
1
Estou confuso com a parte sobre a codificação de URL do arquivo. Carreguei arquivos JPG e PNG como este sem modificá-los, sem problemas.
22613 Deanna Gelbart
1
@DavidGelbart Você está certo. Minha resposta inicial referenciou a -dopção por engano, que precisa da entrada codificada em URL. Eu deveria ter removido isso quando atualizei a resposta para a -Fopção. Obrigado por capturar isso.
jimp
3
@ user956424 No exemplo, defina "imagem" como o nome do seu campo. E algumas linguagens, como PHP, criarão uma matriz se você especificar algo como "imagem []" para as entradas que precisam ser agrupadas.
jimp
1
O que é o @in image=@/..?
Timo
2
@Timo Significa que o conteúdo do campo do formulário nomeado deve ser carregado a partir de um caminho de arquivo. Sem ele, o próprio argumento da string é passado.
jimp
93

Captura do ID do usuário como variável de caminho (recomendado):

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

Captura do ID do usuário como parte do formulário:

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
fonte
16
use -F não precisa ser definido #"Content-Type: multipart/form-data"
William Hu
10
Não consegui que o -F funcionasse corretamente com o separador de ponto e vírgula que você indicou. Em vez disso, tive que fornecer dois argumentos -F redundantes. Como: -F "[email protected]" -F "userid = 1234"
robbpriestley
22

Aqui está a minha solução: eu tenho lido muitos posts e eles foram realmente úteis. Finalmente, escrevi um código para arquivos pequenos, com cURL e PHP, que acho 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

}

Com isso, devemos entrar no "api.endpoint.post", os seguintes vars postados. Você pode testar facilmente com este script e deve receber este debuga na função postFile()na última linha.

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

Deverá funcionar bem, elas podem ser melhores soluções, mas isso funciona e é realmente útil para entender como o mime Boundary e multipart / from-data funciona na biblioteca PHP e cURL.

Libertese
fonte
se você precisar enviar um arquivo não codificado, altere estas linhas $ BODY. = 'Content-Transfer-Encoding: multipart / form-data'. $ eol. $ eol; // colocamos o último conteúdo e 2 $ eol, $ BODY. = file_get_contents ($ file_url). $ eol; // escrevemos o conteúdo do arquivo Base64 e o $ eol para finalizar os dados,
andreah 13/15
8

se você estiver fazendo upload de arquivo binário, como csv, use o formato abaixo para fazer upload de arquivo

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
fonte
4
Eu gostaria de ver um exemplo de um arquivo csv binário.
polis
4
@polis a opção --data-binaryinstrui curla não fazer nenhum pré-processamento dos dados, em vez de --datasinalizar. Para endereçar seu comentário diretamente, observe que o texto também é binário, mas podemos interpretá-lo como caracteres ASCII. Se você realmente deseja um exemplo distinto, pense em um CSV cujos campos contenham emoticons. Seus bytes não mapear diretamente ao texto
Ciprian Tomoiagă
3

O problema que me levou aqui acabou sendo um erro básico do usuário - eu não estava incluindo o @sinal no caminho do arquivo e, portanto, o curl estava postando o caminho / nome do arquivo em vez do conteúdo. O Content-Lengthvalor foi, portanto, 8 em vez dos 479 que eu esperava ver, dada a extensão do meu arquivo de teste.

O Content-Lengthcabeçalho será calculado automaticamente quando o curl ler e publicar o arquivo.

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

... <Comprimento do conteúdo: 479 ...

Publique isso aqui para ajudar outros iniciantes no futuro.

usuário linux shonky
fonte
2

Como alternativa curl, você pode usar o HTTPie , uma CLI, uma ferramenta semelhante ao cURL para humanos.

  1. Instruções de instalação: https://github.com/jakubroztocil/httpie#installation

  2. Então corra:

    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.
fonte
2

Depois de muitas tentativas, este comando funcionou para mim:

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

Aqui está como escapar corretamente os nomes de arquivos arbitrários dos arquivos enviados com bash:

#!/bin/bash
set -eu

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

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

Eu trabalhei com este comando curl -F 'filename=@/home/yourhomedirextory/file.txt' http://yourserver/upload

Shraavan Hebbar
fonte