Envoi d'images à partir d'une Toile éléments à l'aide d'Ajax et PHP $_FILES
J'ai besoin d'être en mesure d'envoyer une image et quelques champs de formulaire à partir d'un côté de client de toile élément à un script PHP, et finir dans les $_POST et $_FILES. Lorsque j'envoie comme ceci:
<script type="text/javascript">
var img = canvas.toDataURL("image/png");
...
ajax.setRequestHeader('Content-Type', "multipart/form-data; boundary=" + boundary_str);
var request_body = boundary + '\n'
+ 'Content-Disposition: form-data; name="formfield"' + '\n'
+ '\n'
+ formfield + '\n'
+ '\n'
+ boundary + '\n'
+ 'Content-Disposition: form-data; name="async-upload"; filename="'
+ "ajax_test64_2.png" + '"' + '\n'
+ 'Content-Type: image/png' + '\n'
+ '\n'
+ img
+ '\n'
+ boundary;
ajax.send(request_body);
</script>
$_POST et $_FILES à la fois de revenir peuplée, mais les données de l'image dans la variable $_FILES encore à décoder comme ceci:
$loc = $_FILES['async-upload']['tmp_name'];
$file = fopen($loc, 'rb');
$contents = fread($file, filesize($loc));
fclose($file);
$filteredData=substr($contents, strpos($contents, ",")+1);
$unencodedData=base64_decode($filteredData);
...afin de l'enregistrer en tant que lisible PNG. Ce n'est pas une option que je suis en train de passer de l'image de WordPress, media_handle_upload() de la fonction, ce qui implique un indice de la variable $_FILES pointant vers une image lisible. Je ne peux pas décoder, enregistrer et modifier 'tmp_name' en conséquence, comme il échappe à tout contrôle de sécurité.
Donc, j'ai trouvé ceci:
http://www.webtoolkit.info/javascript-base64.html
et a essayé de faire le décoder sur le côté client:
img_split = img.split(",",2)[1];
img_decoded = Base64.decode( img_split );
mais pour une raison que je n'ai toujours pas retrouver avec un fichier lisible quand il arrive à l'PHP.
Donc la question est: "Pourquoi?" ou "Ce que je fais mal?" ou "Est-ce même possible?" 🙂
Toute aide grandement appréciée!
Grâce,
Kane
- J'avais mis un
Content-Transfer-Encoding: base64
& regardez cette réponse perdre le préfixe n'ai pas testé si. - Content-Transfer-Encoding, bien que valide d'un en-tête MIME, n'est pas valide en-tête HTTP. Voir cette annexe de la spec.
- Accusé de réception de mon erreur. Se fait-il que je n'ai jamais eu besoin de créer manuellement un fichier à télécharger 🙂
- vous n'êtes pas le seul. C'est tout nouveau et le monde déroutant pour moi 🙂
Vous devez vous connecter pour publier un commentaire.
Bâtiment sur Nathan est excellente réponse, j'ai été en mesure de finnagle de sorte qu'il est toujours en cours à travers jQuery.ajax. Il suffit d'ajouter ce pour la requête ajax:
Fondamentalement, vous avez juste retourner un objet xhr qui est surchargé de sorte que "envoyer" signifie "sendAsBinary". JQuery fait la bonne chose.
Malheureusement, ce n'est pas possible en JavaScript sans l'intermédiaire d'encodage. Pour comprendre pourquoi, supposons que vous base64 décodé et affiché les données, comme vous l'avez décrit dans votre exemple. Les premières lignes dans l'hex d'un valide fichier PHP pourrait ressembler à ceci:
Si vous avez regardé la même gamme de hex de votre téléchargement de fichier PNG, il devrait ressembler à ceci:
Les différences sont subtiles. Comparer les deuxième et troisième colonnes de la deuxième ligne. Dans le fichier valide, les quatre octets sont
0x00
0x80
0x00
0x00
. Dans votre fichier téléchargé, même de quatre octets sont0x00
0xc2
0x80
0x00
. Pourquoi?JavaScript chaînes UTF. Cela signifie que tout type de valeurs binaires (0 à 127) sont codés avec un octet. Cependant, rien de 128-2047 obtient deux octets. Extra
0xc2
dans le fichier téléchargé est un artefact de cet encodage multi-octets. Si vous voulez savoir exactement pourquoi cela se produit, vous pouvez en savoir plus sur L'encodage UTF sur Wikipédia.Vous ne pouvez pas empêcher que cela se passe avec les chaînes de caractères JavaScript, donc vous ne pouvez pas télécharger ces données binaires via AJAX sans l'aide de base64.
EDIT: Après quelques creuser, c'est possible avec certains navigateurs modernes. Si le navigateur prend en charge
XMLHttpRequest.prototype.sendAsBinary
(Firefox 3 et 4), vous pouvez l'utiliser pour envoyer l'image, comme ceci:Pour les navigateurs qui n'ont pas
sendAsBinary
, mais n'ontUint8Array
(Chrome et WebKit), vous pouvez polyfill il de la sorte:Content-Transfer-Encoding: base64
(merci @Wrikken!) mais comme vous l'avez dit c'est pas un en-tête valide donc le navigateur refuse de le faire ("a Refusé d'dangereuses en-tête"). Est-il quelque chose que je peux mettre aprèsContent-Disposition
qui le ferait? Expérimenté pendant un certain temps, mais n'ai rien trouvé qui fonctionne.Content-Disposition: form-data; name="formfield"; filename="picture.png" Content-type: image/png
puis plus rien. Où est-ce que les données? Signifie-t-il, je devrais être de transférer les données de l'image réelle, dans une demande séparée ou quelque chose? Désolé, je suis très nouveau à ce genre de choses et n'ont pas encore trouver un bon tuto...