Comment recevoir un fichier via HTTP mis avec PHP

C'est quelque chose qui me dérange depuis un moment.. Je suis en train de créer une API RESTful qui doit recevoir des fichiers à certaines occasions.

en utilisant HTTP POST, on peut lire data from $_POST et files from $_FILES.

en utilisant HTTP GET, on peut lire data from $_GET et files from $_FILES.

cependant, si vous utilisez HTTP PUT, AFAIK la seule façon de lire les données est d'utiliser le php://input stream.

Tous bon et bien, jusqu'à ce que je veux envoyer un fichier via HTTP PUT. Maintenant, le php: / / le flux d'entrée ne fonctionne plus comme prévu, puisqu'il y a aussi un fichier.

Voici comment j'ai actuellement lire des données sur un METTRE de demande:

(qui fonctionne très bien tant qu'il n'y a pas des fichiers publiés)

$handle  = fopen('php://input', 'r');
$rawData = '';
while ($chunk = fread($handle, 1024)) {
    $rawData .= $chunk;
}

parse_str($rawData, $data);

quand je sors alors rawData, il montre

-----ZENDHTTPCLIENT-44cf242ea3173cfa0b97f80c68608c4c
Content-Disposition: form-data; name="image_01"; filename="lorem-ipsum.png"
Content-Type: image/png; charset=binary

�PNG
���...etc etc...
���,
-----ZENDHTTPCLIENT-8e4c65a6678d3ef287a07eb1da6a5380
Content-Disposition: form-data; name="testkey"

testvalue
-----ZENDHTTPCLIENT-8e4c65a6678d3ef287a07eb1da6a5380
Content-Disposition: form-data; name="otherkey"

othervalue

est-ce que quelqu'un sait comment recevoir correctement des fichiers via HTTP PUT, ou comment analyser des fichiers hors du flux php://input?

===== mise à JOUR #1 =====

j'ai essayé la méthode ci-dessus, n'ont pas vraiment idée de ce que je peux faire d'autre.

Je n'ai pas eu d'erreurs en utilisant cette méthode, en plus du fait que je n'obtiens pas le résultat désiré des données et des fichiers postés.

= ====mise à jour #2======

j'envoie cette requête de test en utilisant Zend_Http_Client, comme suit:: (Je n'ai eu aucun problème avec Zend_Http_Client so loin)

$client = new Zend_Http_Client();
$client->setConfig(array(
    'strict'       => false,
    'maxredirects' => 0,
    'timeout'      => 30)
);
$client->setUri( 'http://...' );
$client->setMethod(Zend_Http_Client::PUT);
$client->setFileUpload( dirname(__FILE__) . '/files/lorem-ipsum.png', 'image_01');
$client->setParameterPost(array('testkey' => 'testvalue', 'otherkey' => 'othervalue');
$client->setHeaders(array(
    'api_key'    => '...',
    'identity'   => '...',
    'credential' => '...'
));

= = = = = SOLUTION ======

il S'avère que j'ai fait quelques fausses hypothèses, principalement que HTTP PUT serait similaire à HTTP POST. Comme vous pouvez le lire ci-dessous, DaveRandom m'a expliqué que HTTP PUT N'est pas destiné à transférer plusieurs fichiers sur la même requête.

j'ai maintenant déplacé le transfert de formdata du corps vers l'url querystring. Le corps contient maintenant le contenu d'un seul fichier.

Pour en savoir plus information, Lire la réponse de DaveRandom. C'est épique.

28
demandé sur Maurice 2012-08-17 16:25:54

5 réponses

Les données que vous montrez ne représentent pas valide METTRE du corps de la requête (bon, c' , mais j'en doute fortement). Ce qu'il montre est un multipart/form-data request body - le type MIME utilisé lors du téléchargement de fichiers via HTTP POST via un formulaire HTML.

les requêtes PUT devraient exactement compléter la réponse à une requête GET - ils vous envoient le contenu du fichier dans le corps du message, et rien d'autre.

essentiellement ce que je dis est que ce n'est pas votre code pour recevoir le fichier qui est erroné, c'est le code qui fait la demande - le code client est incorrect, pas le code que vous montrez ici (bien que le parse_str() appel est inutile).

si vous expliquez ce qu'est le client (un navigateur, un script sur un autre serveur, etc.) alors je peux vous aider à aller plus loin. Comme c'est le cas, la méthode de requête appropriée pour le corps de requête que vous représentez est POST, pas PUT.


prenons un peu de recul par rapport au problème, et regardons Protocole HTTP en général-en particulier le côté client request-espérons que cela vous aidera à comprendre comment tout cela est censé fonctionner. Tout d'abord, un peu d'histoire (si cela ne vous intéresse pas, n'hésitez pas à sauter cette section).

Histoire

HTTP a été conçu à l'origine comme un mécanisme pour récupérer des documents HTML à partir de serveurs distants. Au début, il ne soutenait efficacement que la méthode GET, par laquelle le client demander un document par son nom et le serveur devrait retourner au client. La première spécification publique pour HTTP, appelée HTTP 0.9, est apparue en 1991 - et si vous êtes intéressé, vous pouvez la lire ici.

la spécification HTTP 1.0 (formalisée en 1996 avec RFC 1945) a considérablement élargi les possibilités du protocole, en ajoutant les méthodes de tête et de poteau. Il n'était pas compatible avec HTTP 0.9, en raison d'un changement de format du réponse - un code de réponse a été ajouté, de même que la capacité d'inclure des métadonnées pour le document retourné sous forme de en-têtes de format MIME-paires de données clé/valeur. HTTP 1.0 a également abstrait le protocole de HTML, permettant le transfert de fichiers et de données dans d'autres formats.

HTTP 1.1, la forme du protocole qui est presque exclusivement utilisé aujourd'hui est construite sur HTTP 1.0 et a été conçue pour être rétrocompatible avec les implémentations HTTP 1.0. Il a été standardisé en 1999 avec RFC 2616. Si vous êtes un développeur travaillant avec HTTP, apprenez à connaître ce document - c'est votre bible. Comprendre pleinement vous donnera un avantage considérable sur vos pairs qui n'en ont pas.

faire le point déjà

HTTP fonctionne sur une architecture requête-réponse - le client envoie un message de requête au serveur, le serveur renvoie un message de réponse au client.

Une demande le message comprend une méthode, une URI et éventuellement un certain nombre d'en-têtes. La méthode de la demande est ce à quoi cette question se rapporte, c'est donc ce que je vais couvrir le plus en profondeur ici - mais d'abord, il est important de comprendre exactement ce que nous voulons dire lorsque nous parlons de L'URI de la demande.

L'URI est l'emplacement sur le serveur de la ressource que nous recherchons. En général, il se compose d'un chemin composant, et éventuellement un chaîne de requête. Il y a les circonstances dans lesquelles d'autres composants peuvent être présents, mais pour des raisons de simplicité, nous les ignorerons pour l'instant.

imaginons que vous tapez http://server.domain.tld/path/to/document.ext?key=value dans la barre d'adresse de votre navigateur. Le navigateur démonte cette chaîne, et détermine qu'il doit se connecter à un serveur HTTP à server.domain.tld, et demander le document à /path/to/document.ext?key=value.

la requête HTTP 1.1 générée ressemblera (au moins) à ceci:

GET /path/to/document.ext?key=value HTTP/1.1
Host: server.domain.tld

la première partie de La la demande est le mot GET - C'est la méthode de requête. La partie suivante est le chemin vers le fichier que nous demandons - C'est L'URI de la requête. À la fin de cette première ligne est un identificateur indiquant la version du protocole à utiliser. Sur la ligne suivante vous pouvez voir un en-tête au format MIME, appelé Host. HTTP 1.1 exige que le Host: l'en-tête doit être inclus avec chaque requête. C'est le seul en-tête dont ceci est vrai.

L'URI de demande est divisé en deux parties - tout à gauche du point d'interrogation ? est le chemin, tout à droite, c'est la chaîne de requête.

Méthodes De Demande De

RFC 2616 (HTTP/1.1) définit 8 méthodes de demande de.

OPTIONS

la méthode des OPTIONS est rarement utilisée. Il est conçu comme un mécanisme pour déterminer quel type de fonctionnalité le serveur prend en charge avant tenter de consommer un service le serveur peut fournir.

hors de ma tête, le seul endroit dans l'usage assez commun que je peux penser à l'endroit où il est utilisé est lorsque l'ouverture de documents dans Microsoft office directement sur HTTP à partir D'Internet Explorer-Office enverra une demande D'OPTIONS au serveur pour déterminer si elle soutient la méthode PUT pour L'URI spécifique, et si elle le fait, il ouvrira le document d'une manière qui permet à l'utilisateur de sauvegarder leurs modifications au document directement de retour sur le serveur distant. Cette fonctionnalité est étroitement intégrée dans ces applications Microsoft spécifiques.

GET

C'est de loin la méthode la plus commune dans l'usage quotidien. Chaque fois que vous chargez un document régulier dans votre navigateur web, ce sera une requête GET.

la méthode GET demande que le serveur renvoie un document spécifique. Les seules données qui doivent être transmises au serveur d'informations que le serveur exige de déterminez quel document doit être retourné. Cela peut inclure des informations que le serveur peut utiliser pour générer dynamiquement le document, qui est envoyé sous forme d'en-têtes et/ou de chaîne de requête dans L'URI de la requête. Pendant que nous sommes sur le sujet - les Cookies sont envoyés dans les en-têtes de demande.

HEAD

cette méthode est identique à la méthode GET, avec une différence - le serveur ne retournera pas le document demandé, si retournera seulement les en-têtes qui seraient inclus dans la réponse. Cela est utile pour déterminer, par exemple, si un document existe sans avoir à transférer et à traiter le document en entier.

POST

C'est la deuxième méthode plus couramment utilisée, et sans doute la plus complexe. Les requêtes de méthode POST sont presque exclusivement utilisées pour invoquer certaines actions sur le serveur qui peuvent changer son état.

une requête POST, contrairement à GET et HEAD, peut (et inclut habituellement) certaines données dans le corps du message de demande. Ces données peuvent être dans n'importe quel format, mais le plus souvent il s'agit d'une chaîne de requête (dans le même format qu'il apparaîtrait dans L'URI de la requête) ou un message multipartie qui peut communiquer des paires de clés/valeurs avec des pièces jointes de dossier.

de nombreux formulaires HTML utilisent la méthode POST. Pour télécharger des fichiers à partir d'un navigateur, vous devez utiliser la méthode POST pour votre formulaire.

la méthode de POST est sémantiquement incompatible avec les API RESTful parce qu'elle n'est pas quantité. C'est-à-dire qu'une seconde requête POST identique peut entraîner un changement supplémentaire de l'état du serveur. Cela contredit la contrainte" apatride " du repos.

PUT

ceci complète directement GET. Lorsqu'une requête GET indique que le serveur doit renvoyer le document à l'emplacement spécifié par L'URI de la requête dans le corps de réponse, la méthode PUT indique que le serveur doit stocker les données dans la requête corps à l'endroit spécifié par L'URI de demande.

DELETE

indique que le serveur doit détruire le document à l'endroit indiqué par L'URI de la requête. Très peu d'implémentations de serveur HTTP sur Internet vont effectuer une action quand elles reçoivent une requête de suppression, pour des raisons assez évidentes.

TRACE

ceci fournit un mécanisme de niveau application-layer pour permettre aux clients d'inspecter la demande qu'il a envoyé comme il semble au moment où il atteint le serveur de destination. Ceci est surtout utile pour déterminer l'effet que tout serveur proxy entre le client et le serveur de destination peut avoir sur le message de requête.

CONNECT

HTTP 1.1 réserve le nom d'une méthode CONNECT, mais ne définit pas son usage, ni même son but. Certaines implémentations de serveurs mandataires ont depuis utilisé la méthode CONNECT pour faciliter le tunnelling HTTP.

41
répondu DaveRandom 2012-08-17 15:43:32

Je n'ai jamais essayé D'utiliser PUT (GET POST and FILES were sufficient for my needs) mais cet exemple vient des Docs php donc il pourrait vous aider (http://php.net/manual/en/features.file-upload.put-method.php):

<?php
/* PUT data comes in on the stdin stream */
$putdata = fopen("php://input", "r");

/* Open a file for writing */
$fp = fopen("myputfile.ext", "w");

/* Read the data 1 KB at a time
   and write to the file */
while ($data = fread($putdata, 1024))
  fwrite($fp, $data);

/* Close the streams */
fclose($fp);
fclose($putdata);
?>
6
répondu kjurkovic 2012-08-17 12:30:17

Voici la solution que j'ai trouvée la plus utile.

$put = array(); parse_str(file_get_contents('php://input'), $put);

$put sera un tableau, tout comme vous êtes habitués à voir dans $_POST, sauf que maintenant vous pouvez suivre le protocole HTTP true REST.

2
répondu Daniel Sikes 2016-02-19 04:16:01

il suffit de suivre ce qui est dit dans le DOC:

<?php
/* PUT data comes in on the stdin stream */
$putdata = fopen("php://input", "r");

/* Open a file for writing */
$fp = fopen("myputfile.ext", "w");

/* Read the data 1 KB at a time
   and write to the file */
while ($data = fread($putdata, 1024))
  fwrite($fp, $data);

/* Close the streams */
fclose($fp);
fclose($putdata);
?>

devrait lire le fichier entier qui est sur le flux PUT et le sauvegarder localement, alors vous pouvez faire ce que vous voulez avec lui.

1
répondu Neal 2012-08-17 13:02:36

Utiliser POST et inclure un en - tête X pour indiquer la méthode réelle (mis dans ce cas). Habituellement, c'est ainsi que l'on fonctionne autour d'un pare-feu qui n'autorise pas d'autres méthodes que GET et POST. Il suffit de déclarer PHP buggy (puisqu'il refuse de gérer les charges utiles multipart PUT, il est buggy), et de le traiter comme un pare-feu obsolète/draconien.

les opinions quant à ce que PUT veut dire par rapport à GET sont justement celles-là, les opinions. Le HTTP ne fait pas une telle exigence. Il indique simplement "équivalent".. c'est au concepteur de déterminer ce que "équivalent". Si votre design peut accepter un téléchargement multi-fichier PUT et produire une représentation "équivalente" pour un GET subséquent pour la même ressource, c'est très bien et dandy, à la fois techniquement et philosophiquement, avec les spécifications HTTP.

1
répondu user4157069 2014-10-18 16:28:35