web-dev-qa-db-fra.com

Comment obtenir un fichier à partir de données POST dans le script CGI Bash?

J'essaie de publier un fichier en utilisant cURL et de le recevoir de l'autre côté via un script CGI Bash et de le stocker avec le même nom. Une fois le téléchargement terminé, diff entre le fichier d'origine et le fichier reconstruit doit retourner zéro.

La façon dont cURL envoie les données:

curl --request POST --data-binary "@dummy.dat" 127.0.0.1/cgi-bin/upload-rpm

Script récepteur:

#!/bin/bash

echo "Content-type: text/html"
echo ""

echo '<html>'
echo '<head>'
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
echo '<title>Foo</title>'
echo '</head>'
echo '<body>'

echo "<p>Start</p>"

if [ "$REQUEST_METHOD" = "POST" ]; then
    echo "<p>Post Method</p>"
    if [ "$CONTENT_LENGTH" -gt 0 ]; then
        echo "<p>Size=$CONTENT_LENGTH</p>"
        while read -n 1 byte -t 3; do echo -n -e "$byte" >> ./foo.dat ; done
    fi
fi
echo '</body>'
echo '</html>'

exit 0

Mais ça ne marche pas. Le fichier n'est pas créé côté serveur. Et comment puis-je obtenir le nom du fichier?

14
sorush-r

tant que vous téléchargez toujours exactement 1 fichier à l'aide du multipart/form-data format et le client met toujours le Content-Type en tant que dernier en-tête du téléchargement du fichier (ce que semble toujours faire curl), cela semble fonctionner:

#!/bin/bash

echo "Content-type: text/html"
echo ""

echo '<html>'
echo '<head>'
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
echo '<title>Foo</title>'
echo '</head>'
echo '<body>'

echo "<p>Start</p>"

if [ "$REQUEST_METHOD" = "POST" ]; then
    echo "<p>Post Method</p>"
    if [ "$CONTENT_LENGTH" -gt 0 ]; then
    in_raw=`cat`
    boundary=$(echo -n "$in_raw" | head -1 | tr -d '\r\n');
    filename=$(echo -n "$in_raw" | grep --text --max-count=1 -oP "(?<=filename=\")[^\"]*");
    file_content=$(echo -n "$in_raw" | sed '1,/Content-Type:/d' | tail -c +3 | head --lines=-1 | head --bytes=-4  );
    echo "boundary: $boundary"
    echo "filename: $filename"
    #echo "file_content: $file_content"
    fi
fi
echo '</body>'
echo '</html>'

exit 0

exemple d'invocation de téléchargement (que j'ai utilisé pour déboguer ce qui précède):

curl http://localhost:81/cgi-bin/wtf.sh -F "[email protected]"
  • avec tout ce qui est dit, CECI IS INSANITY! bash est absolument pas adapté pour gérer les données binaires, telles que les téléchargements de fichiers http, utilisez autre chose. (PHP? Python? )
8
hanshenrik

La façon dont curl envoie les données

Avec champs:

curl --data "param1=value1&param2=value2" https://example.com/resource.cgi

Avec des champs spécifiés individuellement:

curl --data "param1=value1" --data "param2=value2" https://example.com/resource.cgi

Multipart:

curl --form "[email protected]" https://example.com/resource.cgi

Multipart avec des champs et un nom de fichier:

curl --form "[email protected];filename=desired-filename.txt" --form param1=value1 --form param2=value2 https://example.com/resource.cgi

Pour un HTTP RESTful POST contenant XML:

curl -X POST -d @filename.txt http://example.com/path/to/resource --header "Content-Type:text/xml"

ou pour JSON, utilisez ceci:

curl -X POST -d @filename.txt http://example.com/path/to/resource --header "Content-Type:application/json"

Cela lira le contenu du fichier nommé filename.txt et l'enverra comme demande de publication.

Pour plus d'informations, voir

Lecture de données HTTP POST à l'aide de BASH

Comment obtenir la longueur du contenu:

if [ "$REQUEST_METHOD" = "POST" ]; then
    if [ "$CONTENT_LENGTH" -gt 0 ]; then
        read -n $CONTENT_LENGTH POST_DATA <&0
        echo "$CONTENT_LENGTH"
    fi
fi

Pour enregistrer un fichier binaire ou texte:

boundary=$(export | \
    sed '/CONTENT_TYPE/!d;s/^.*dary=//;s/.$//')
filename=$(echo "$QUERY_STRING" | \
    sed -n '2!d;s/\(.*filename=\"\)\(.*\)\".*$/\2/;p')
file=$(echo "$QUERY_STRING" | \
    sed -n "1,/$boundary/p" | sed '1,4d;$d')

Le fichier téléchargé est maintenant contenu dans la variable $ file et le nom de fichier dans la variable $ filename.

7
Bharata