web-dev-qa-db-fra.com

Comment télécharger une image avec un formulaire simple?

J'écris un plugin dans ma page de profil et je veux créer un téléchargement via le bouton 'Parcourir' et le champ 'Dir' qui chargeront et renverront l'URL de l'image. Et je ne veux pas utiliser le téléchargement multimédia.

J'avais l'habitude de lire Définir l'image vedette Front Frontend ? et je ne comprends pas le code Aidez-moi à résoudre ce problème?

3
MonoGot

Il y a plusieurs parties.

Vous devez ajouter une enctype au formulaire de profil.

function edit_form_type_wpse_98375() {
    echo ' enctype="multipart/form-data"';
}
add_action('user_edit_form_tag','edit_form_type_wpse_98375');

Ajoutez ensuite un champ au formulaire.

function user_fields_wpse_98375($profileuser) {
  $_profile_photo = get_user_meta($profileuser->data->ID,'_profile_photo',true);

  echo '<h3>'.__('Additional User Data',THEME_TEXTDOMAIN).'</h3>';
    echo '<tr class="show-admin-bar">';
      echo '<th scope="row">'.__('Profile Photo', THEME_TEXTDOMAIN).'</th>';
      echo '<td'.$tspan.'>';
        echo '<fieldset>';
          echo '<legend class="screen-reader-text"><span>'.__('Profile Photo', THEME_TEXTDOMAIN).'</span></legend>';
          echo '<label for="profile_photo">';
            echo '<input name="profile_photo" type="file" id="profile_photo" value="" />';
          echo '</label><br />';
        echo '</fieldset>';
      echo '</td>';
    echo '</tr>';
  echo '</table>';
}
add_action('show_user_profile', 'user_fields_wpse_98375');
add_action('edit_user_profile', 'user_fields_wpse_98375');

Et puis enregistrez les données.

function save_user_custom($id=false) {
  global $_FILES,$_POST;
  if (false === $id) return false;

  // save image
  if (isset($_FILES)) {
    if (isset($_FILES['profile_photo'])){
      if (0 === $_FILES['profile_photo']['error']) {
        // This is where you save the file
        // Maybe use wp_handle_upload
        // Or use the Filesystem API
        // not sure what you want to do
      }
    }
    unset($up);
  }
}
add_action('personal_options_update','save_user_custom');
add_action('edit_user_profile_update','save_user_custom');

wp_handle_upload est probablement le plus simple. Du Codex:

if ( ! function_exists( 'wp_handle_upload' ) ) 
    require_once( ABSPATH . 'wp-admin/includes/file.php' );
$uploadedfile = $_FILES['file'];
$upload_overrides = array( 'test_form' => false );
$movefile = wp_handle_upload( $uploadedfile, $upload_overrides );
if ( $movefile ) {
    echo "File is valid, and was successfully uploaded.\n";
    var_dump( $movefile);
} else {
    echo "Possible file upload attack!\n";
}

Le type d'entrée file fonctionne (généralement) comme n'importe quelle autre entrée de formulaire. Si vous lui donnez un nom comme my_uploads['profile_photo'] au lieu de profile_photo, vous obtiendrez un tableau. Cela sera reflété dans la variable $_FILES lors du traitement de la soumission du formulaire. Vous pouvez ajouter autant d'entrées file que vous le souhaitez en construisant ce tableau, ou même simplement en leur donnant des noms différents. Pour ce qui est d'ajouter dynamiquement des entrées file, c'est assez simple en Javascript.

Référence

http://codex.wordpress.org/Function_Reference/wp_handle_upload
http://codex.wordpress.org/Filesystem_API

6
s_ha_dum

Je suppose que vous n'êtes pas très expérimenté avec PHP et les fichiers téléchargés. Je commence donc par quelques notions de base et je termine par un cours simple.

Si vous n'avez pas déjà lu le notions de base sur le téléchargement de fichiers avec PHP, faites-le maintenant. Personne ne l'expliquera ici pour vous, c'est hors sujet.

Commençons par le formulaire HTML de base

if( empty( $_FILES ) ) {
global $pagenow;
$url = admin_url( $pagenow );
?>
<!-- The data encoding type, enctype, MUST be specified as below -->
<form enctype="multipart/form-data" action="<?php echo $url; ?>" method="POST">
    <!-- MAX_FILE_SIZE must precede the file input field -->
    <input type="hidden" name="MAX_FILE_SIZE" value="30000" />
    <!-- Name of input element determines name in $_FILES array -->
    Send this file: <input name="userfile" type="file" />
    <input type="submit" value="Send File" />
</form>
<?php
} else {
    $imageupload = new File_Upload();
    $attachment_id = $imageupload->create_attachment();
    var_dump( $attachment_id );
}

C'est très simple. Si le tableau superglobal $_FILES est vide, cela signifie qu'aucun fichier n'a été téléchargé, affichez le formulaire de téléchargement. S'il n'est pas vide, traitez le téléchargement. J'utilise admin_url() et le $pagenow var pour créer l'URL de l'action. Si vous utilisez le formulaire de téléchargement sur l’interface, vous devez utiliser home_url() ou quelque chose de similaire.

Après avoir envoyé le fichier avec HTML, vous devez traiter les fichiers téléchargés. Cela sera fait dans la classe File_Upload.

class File_Upload
{
    /**
     * Index key from upload form
     * @var string
     */
    public $index_key = '';

    /**
     * Copy of superglobal array $_FILES
     * @var array
     */
    public $files = array();

    /**
     * Constructor
     * Setup files array and guess index key
     */
    public function __construct(){

        if ( isset( $_FILES ) && ! empty( $_FILES ) ) {
            $this->files = $_FILES;
            $this->guess_index_key();
        }

    }

    /**
     * Set/overwrites the index key
     * Converts $name with type casting (string)
     *
     * @param   string  $name   Name of the index key
     * @return  string  ::name  Name of the stored index key
     */
    public function set_field_name_for_file( $name = '' ) {
        $this->index_key = ( ! empty( $name ) ) ? (string) $name : '';
        return $this->index_key;
    }

    /**
     * Converts uploaded file into WordPress attachment
     *
     * @return  boolean     Whether if the attachment was created (true) or not (false)
     */
    public function create_attachment(){

        // move the uploaded file from temp folder and create basic data
        $imagedata = $this->handle_uploaded_file();

        // if moving fails, stop here
        /*
         * For Production
         * Set and return an error object with WP_Error()
         */
        if ( empty( $imagedata ) )
            return false;

        /*
         * For Production
         * Check if $imagedata contains the expected (and needed)
         * values. Every method could fail and return malicious data!!
         */
        $filename = $imagedata['filename'];

        // create the attachment data array
        $attachment = array(
                'guid'           => $imagedata['url'] . '/' . $filename,
                'post_mime_type' => $imagedata['type'],
                'post_title'     => preg_replace('/\.[^.]+$/', '', $filename ),
                'post_content'   => '',
                'post_status'    => 'inherit'
        );

        // insert attachment (posttype attachment)
        $attach_id = wp_insert_attachment( $attachment, $filename );

        // you must first include the image.php file
        // for the function wp_generate_attachment_metadata() to work
        require_once( ABSPATH . 'wp-admin/includes/image.php' );

        /*
         * For Production
         * Check $attach_data, wp_generate_attachment_metadata() could fail
         * Check if wp_update_attachment_metadata() fails (returns false),
         * return an error object with WP_Error()
         */
        $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
        wp_update_attachment_metadata( $attach_id, $attach_data );

        return $attach_id;

    }

    /**
     * Handles the upload
     *
     * @return  array   $return_data    Array with informations about the uploaded file
     */
    protected function handle_uploaded_file() {

        // get the basic data
        $return_data = wp_upload_dir();

        // get temporary filepath and filename from $_FILES ($this->files)
        $tmp_file = ( isset( $this->files[$this->index_key] ) && ! empty( $this->files[$this->index_key] ) ) ?
            (string) $this->files[$this->index_key]['tmp_name'] : '';

        $tmp_name = ( isset( $this->files[$this->index_key] ) && ! empty( $this->files[$this->index_key] ) ) ?
            (string) $this->files[$this->index_key]['name'] : '';

        // stop if something went wrong
        if ( empty( $tmp_file ) )
            return false;

        // set filepath
        $filepath = $return_data['filepath'] = $return_data['path'] . '/' . basename( $tmp_name );

        // move uploaded file from temp dir to upload dir
        move_uploaded_file( $tmp_file , $filepath );

        // set filename
        $filename = $return_data['filename'] = basename( $filepath );

        // set filetype
        /*
         * For Production
         * You should really, really check the file extension and filetype on 
         * EVERY upload. If you do not, it is possible to upload EVERY kind of 
         * file including malicious code.
         */
        $type = wp_check_filetype( $filename, null );
        $return_data['file_ext'] = ( isset( $type['ext'] ) && ! empty( $type['ext'] ) ) ?
        $type['ext'] : '';

        $return_data['type'] = ( isset( $type['type'] ) && ! empty( $type['type'] ) ) ?
        $type['type'] : '';

        // return the results
        return $return_data;

    }

    /**
     * Try to fetch the first index from $_FILES
     *
     * @return  boolean     Whether if a key was found or not
     */
    protected function guess_index_key() {

        $keys = array_keys( $_FILES );

        if ( ! empty( $keys ) ) {
            $this->index_key = $keys[0];
            return true;
        }

        return false;

    }

}

Ceci est une classe de base et pas pour la production ! Le téléchargement de fichiers est un sujet très sensible, vous devez valider et nettoyer toutes les données vous-même. Sinon, il est possible que quelqu'un télécharge un code malveillant et pirate votre serveur/blog/site web!

Maintenant, étape par étape, que se passe-t-il où et quand?.

Lors de la création d'une instance de la classe, la classe tente de créer une copie du tableau superglobal $_FILES. Si vous travaillez avec des baies superglobales, ne les touchez pas! Peut-être que d'autres parties du code (plugins ou thèmes) ont également besoin des données qu'elles contiennent. Ensuite, dans le constructeur, nous essayons de deviner la clé d’index. Comme vous le savez, nous pouvons transmettre plus d’un fichier et $_FILES peut contenir des données pour tous les fichiers téléchargés. Si vous devez traiter plusieurs fichiers, passez en boucle sur $_FILES et définissez la clé d'index avec set_field_name_for_file().

// get the indexes from $_FILES
$keys = array_keys( $_FILES );
$upload_processor = new File_Upload();
foreach ( $keys as $key ) {
  $upload_processor->set_field_name_for_file( $key );
  $upload_processor->create_attachment();
}

set_field_name_for_file() est également nécessaire si vous souhaitez capturer un champ spécifique de votre formulaire HTML, sinon la classe utilise le premier index qu'elle peut trouver.

L'étape suivante consiste à gérer le fichier téléchargé. La méthode create_attachment() appelle la méthode protégée handle_uploaded_file(), vous n'avez pas à le faire manuellement. La méthode handle_uploaded_file() collecte et configure simplement certains chemins et noms de fichiers nécessaires.

La dernière étape consiste à créer une pièce jointe. create_atatchment() va configurer toutes les données nécessaires et créer une pièce jointe pour vous. La méthode retourne leIDde la pièce jointe afin que vous puissiez accéder à toutes les données de la pièce jointe avec get_post( [id] ).

$imageupload = new File_Upload();
$attachment_id = $imageupload->create_attachment();
[some other code]        
$attachment = get_post( $attachment_id );
$image_url  = $attachment->guid;

printf( '<p><img src="%s"></p>', $image_url );

Quelques mots sur le traitement des erreurs

J'ai écrit quelques astuces dans le code de classe pour l'environnement de production. A mentionné ci-dessus, le téléchargement de fichiers est un sujet très sensible. Vous devez vérifier, valider et assainir vraiment tout. WordPress a une construction dans la gestion des erreurs avec WP_Error(). Utilise le! Et vérifiez si le téléchargement a été un succès avec is_wp_error().

PHP peut configurer des messages d'erreur expliquant pourquoi le téléchargement a échoué. Mais PHP ne renvoie pas de messages en texte clair, il renvoie des codes d'erreur. Une méthode pour convertir ces codes en texte clair pourrait ressembler à ceci:

fonction protégée guess_upload_error ($ err = 0) {

$errcodes = array(
    'Unknown error',
    'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
    'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
    'The uploaded file was only partially uploaded.',
    'No file was uploaded.',
    'Missing a temporary folder.',
    'Failed to write file to disk.',
    'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with phpinfo() may help.'
);

return ( isset( $errcodes[$err] ) ) ?
    $errcodes[$err] : 'Unknown error';

}

Vous devez vérifier si le téléchargement a réussi, cela pourrait ressembler à ceci (dans la méthode handle_upload_file())

// stop if something went wrong
if ( empty( $tmp_file ) ) {

    $code = ( isset( $this->files[$this->index_key]['error'] ) ) ?
        $this->files[$this->index_key]['error'] : 0;

    $msg = $this->guess_upload_error( $code );

    return new WP_Error( 'uploaderror', 'Upload failed with message: ' . $msg );
}

Et puis vous devez gérer l'erreur lorsque vous appelez la classe

if ( empty( $_FILES ) ) {

global $pagenow;
$url = admin_url( $pagenow );
?>
<!-- HTML form-->
<?php
} else {
    $imageupload = new File_Upload();
    $attachment_id = $imageupload->create_attachment();

    if ( is_wp_error( $attachment_id ) ) {

        echo '<ol>';
        foreach ( $attachment_id->get_error_messages() as $err )
            printf( '<li>%s</li>', $err );
        echo '</ol>';

    } else {

        // do something with the created attachment
        $attachment = get_post( $attachment_id );
        $image_url  = $attachment->guid;

        printf( '<p><img src="%s"></p>', $image_url );

    }
}

Rappelez-vous toujours : Ne laissez jamais un téléchargement échoué ne pas être traité!

4
Ralf912