¿Cómo subir una imagen con un formulario simple?

5 may 2013, 13:48:15
Vistas: 33.5K
Votos: 4

Estoy escribiendo un plugin en mi página de perfil y quiero crear una subida de archivos mediante un botón 'Examinar' y un campo 'Directorio' que subirá y devolverá la URL de la imagen. No quiero usar media-upload.

He leído Establecer Imagen Destacada desde Formulario Frontend pero no entiendo el código. ¿Me pueden ayudar a resolver este problema?

Here's a simple solution:
<form method="post" enctype="multipart/form-data">
    <input type="file" name="imagen" accept="image/*" />
    <input type="submit" value="Subir Imagen" name="submit" />
</form>
// Procesar la subida de la imagen
if(isset($_POST['submit'])) {
    // Verificar si se seleccionó un archivo
    if(!empty($_FILES['imagen']['name'])) {
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        require_once(ABSPATH . 'wp-admin/includes/file.php');
        require_once(ABSPATH . 'wp-admin/includes/media.php');
        
        // Subir el archivo y obtener el ID del adjunto
        $attachment_id = media_handle_upload('imagen', 0);
        
        if(is_wp_error($attachment_id)) {
            echo "Error al subir la imagen";
        } else {
            // Obtener la URL de la imagen
            $imagen_url = wp_get_attachment_url($attachment_id);
            echo "Imagen subida correctamente: " . $imagen_url;
        }
    }
}
0
Todas las respuestas a la pregunta 3
2

Hay varias partes.

Necesitas agregar un enctype al formulario de perfil.

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

Luego agregar un campo al formulario.

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

  echo '<h3>'.__('Datos Adicionales de Usuario',THEME_TEXTDOMAIN).'</h3>';
    echo '<tr class="show-admin-bar">';
      echo '<th scope="row">'.__('Foto de Perfil', THEME_TEXTDOMAIN).'</th>';
      echo '<td'.$tspan.'>';
        echo '<fieldset>';
          echo '<legend class="screen-reader-text"><span>'.__('Foto de Perfil', 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');

Y luego guardar los datos.

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

  // guardar imagen
  if (isset($_FILES)) {
    if (isset($_FILES['profile_photo'])){
      if (0 === $_FILES['profile_photo']['error']) {
        // Aquí es donde guardas el archivo
        // Quizá usar wp_handle_upload
        // O usar la Filesystem API
        // no estoy seguro de lo que quieres hacer
      }
    }
    unset($up);
  }
}
add_action('personal_options_update','save_user_custom');
add_action('edit_user_profile_update','save_user_custom');

wp_handle_upload es probablemente lo más simple. Del 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 "El archivo es válido y fue subido exitosamente.\n";
    var_dump( $movefile);
} else {
    echo "¡Posible ataque de subida de archivo!\n";
}

El tipo de input file funciona (mayormente) como cualquier otro input de formulario. Si le das un nombre como my_uploads['profile_photo'] en lugar de solo profile_photo obtendrás un array. Esto se reflejará en la variable $_FILES cuando proceses el envío del formulario. Puedes agregar tantos inputs file como quieras construyendo ese array, o simplemente dándoles nombres diferentes. En cuanto a agregar dinámicamente inputs file, eso es Javascript bastante simple.

Referencia

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

5 may 2013 16:36:21
Comentarios

Gracias s_ha_dum ^_^ y este es el ejemplo que realmente quiero http://i165.photobucket.com/albums/u56/zectdropper/Capture_zpsea5fdfc7.png Explicación->el campo de carga puede agregar o eliminar más de uno. Creo que debería guardarse con un array ¿Cómo puedo escribirlos y guardarlos en WordPress?

Monogot Monogot
5 may 2013 17:12:01

Y si tienes que probar errores, puedes usar este hook: user_profile_update_errors

JMau JMau
5 may 2013 18:06:28
1

Supongo que no tienes mucha experiencia con PHP y subidas de archivos. Así que empezaré con algunos conceptos básicos y terminaré con una clase simple.

Si no has leído todavía los conceptos básicos sobre subidas de archivos con PHP, hazlo ahora. Nadie te lo explicará aquí, está fuera de tema.

Empecemos con el formulario HTML básico

if( empty( $_FILES ) ) {
global $pagenow;
$url = admin_url( $pagenow );
?>
<!-- El tipo de codificación de datos, enctype, DEBE especificarse como se muestra a continuación -->
<form enctype="multipart/form-data" action="<?php echo $url; ?>" method="POST">
    <!-- MAX_FILE_SIZE debe preceder al campo de entrada de archivo -->
    <input type="hidden" name="MAX_FILE_SIZE" value="30000" />
    <!-- El nombre del elemento input determina el nombre en el array $_FILES -->
    Enviar este archivo: <input name="userfile" type="file" />
    <input type="submit" value="Enviar Archivo" />
</form>
<?php
} else {
    $imageupload = new File_Upload();
    $attachment_id = $imageupload->create_attachment();
    var_dump( $attachment_id );
}

Esto es bastante simple. Si el array superglobal $_FILES está vacío, significa que no se subió ningún archivo, muestra el formulario de subida. Si no está vacío, procesa la subida. Uso admin_url() y la variable $pagenow para crear la URL de acción. Si usas el formulario de subida en el frontend, debes usar home_url() o algo similar.

Después de enviar el archivo con HTML, debes procesar los archivos subidos. Esto se hará en la clase File_Upload.

class File_Upload
{
    /**
     * Clave de índice del formulario de subida
     * @var string
     */
    public $index_key = '';

    /**
     * Copia del array superglobal $_FILES 
     * @var array
     */
    public $files = array();

    /**
     * Constructor
     * Configura el array files e intenta adivinar la clave de índice
     */
    public function __construct(){

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

    }

    /**
     * Establece/sobrescribe la clave de índice
     * Convierte $name con type casting (string)
     *
     * @param   string  $name   Nombre de la clave de índice
     * @return  string  ::name  Nombre de la clave de índice almacenada
     */
    public function set_field_name_for_file( $name = '' ) {
        $this->index_key = ( ! empty( $name ) ) ? (string) $name : '';
        return $this->index_key;
    }

    /**
     * Convierte el archivo subido en un adjunto de WordPress
     *
     * @return  boolean     Si el adjunto se creó (true) o no (false)
     */
    public function create_attachment(){

        // mover el archivo subido desde la carpeta temporal y crear datos básicos
        $imagedata = $this->handle_uploaded_file();

        // si falla el movimiento, detenerse aquí
        /*
         * Para Producción
         * Establecer y devolver un objeto de error con WP_Error()
         */
        if ( empty( $imagedata ) )
            return false;

        /*
         * Para Producción
         * Verificar si $imagedata contiene los valores esperados (y necesarios)
         * ¡¡Cada método puede fallar y devolver datos maliciosos!!
         */
        $filename = $imagedata['filename'];

        // crear el array de datos del adjunto
        $attachment = array(
                'guid'           => $imagedata['url'] . '/' . $filename,
                'post_mime_type' => $imagedata['type'],
                'post_title'     => preg_replace('/\.[^.]+$/', '', $filename ),
                'post_content'   => '',
                'post_status'    => 'inherit'
        );

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

        // primero debes incluir el archivo image.php
        // para que funcione la función wp_generate_attachment_metadata()
        require_once( ABSPATH . 'wp-admin/includes/image.php' );

        /*
         * Para Producción
         * Verificar $attach_data, wp_generate_attachment_metadata() podría fallar
         * Verificar si wp_update_attachment_metadata() falla (devuelve false),
         * devolver un objeto de error con WP_Error()
         */
        $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
        wp_update_attachment_metadata( $attach_id, $attach_data );

        return $attach_id;

    }

    /**
     * Maneja la subida
     *
     * @return  array   $return_data    Array con información sobre el archivo subido
     */
    protected function handle_uploaded_file() {

        // obtener datos básicos
        $return_data = wp_upload_dir();

        // obtener ruta temporal y nombre de archivo de $_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'] : '';

        // detenerse si algo salió mal
        if ( empty( $tmp_file ) )
            return false;

        // establecer ruta de archivo
        $filepath = $return_data['filepath'] = $return_data['path'] . '/' . basename( $tmp_name );

        // mover archivo subido desde directorio temp a directorio de subidas
        move_uploaded_file( $tmp_file , $filepath );

        // establecer nombre de archivo
        $filename = $return_data['filename'] = basename( $filepath );

        // establecer tipo de archivo
        /*
         * Para Producción
         * Deberías realmente, realmente verificar la extensión y tipo de archivo en
         * CADA subida. Si no lo haces, es posible subir TODO tipo de
         * archivos incluyendo código malicioso.
         */
        $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'] : '';

        // devolver los resultados
        return $return_data;

    }

    /**
     * Intenta obtener el primer índice de $_FILES
     *
     * @return  boolean     Si se encontró una clave o no
     */
    protected function guess_index_key() {

        $keys = array_keys( $_FILES );

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

        return false;

    }

}

Esta es una clase básica y no para producción! Las subidas de archivos son un tema muy delicado, debes validar y sanitizar todos los datos por ti mismo. De lo contrario, es posible que alguien suba código malicioso y hackee tu servidor/blog/sitio web!

Ahora paso a paso qué sucede dónde y cuándo.

Al crear una instancia de la clase, la clase intenta crear una copia del array superglobal $_FILES. Si trabajas con arrays superglobales, ¡no los toques! Quizás otras partes del código (plugins o temas) también necesiten los datos dentro de ellos. Lo siguiente que sucede en el constructor es que intentamos adivinar la clave de índice. Como sabes, podemos pasar más de un archivo y $_FILES puede contener datos para todos los archivos subidos. Si necesitas procesar más de un archivo, recorre $_FILES y establece la clave de índice con set_field_name_for_file()

// obtener los índices de $_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() también es necesario si quieres seleccionar un campo específico de tu formulario HTML, de lo contrario la clase usará el primer índice que encuentre.

El siguiente paso es manejar el archivo subido. El método create_attachment() llama al método protegido handle_uploaded_file(), no tienes que hacerlo manualmente. El método handle_uploaded_file() simplemente recopila y establece algunas rutas y nombres de archivo necesarios.

El último paso es crear un adjunto. create_attachment() configurará todos los datos necesarios y creará un adjunto para ti. El método devuelve el ID del adjunto, por lo que puedes acceder a todos los datos del adjunto con get_post( [id] )

$imageupload = new File_Upload();
$attachment_id = $imageupload->create_attachment();
[algún otro código]        
$attachment = get_post( $attachment_id );
$image_url  = $attachment->guid;

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

Algunas palabras sobre el manejo de errores

Escribí algunas pistas en el código de la clase para entornos de producción. Como mencioné anteriormente, las subidas de archivos son un tema muy delicado. Debes verificar, validar y sanitizar absolutamente todo. WordPress tiene un manejo de errores incorporado con WP_Error(). ¡Úsalo! Y verifica si la subida fue exitosa con is_wp_error().

PHP puede configurar algunos mensajes de error si la subida falló. Pero PHP no devuelve mensajes de texto claro, devuelve códigos de error. Un método para convertir estos códigos en texto claro podría verse así:

protected function guess_upload_error( $err = 0 ) {

    $errcodes = array(
        'Error desconocido',
        'El archivo subido excede la directiva upload_max_filesize en php.ini.',
        'El archivo subido excede la directiva MAX_FILE_SIZE especificada en el formulario HTML.',
        'El archivo subido fue solo parcialmente subido.',
        'No se subió ningún archivo.',
        'Falta una carpeta temporal.',
        'Falló al escribir el archivo en el disco.',
        'Una extensión de PHP detuvo la subida del archivo. PHP no proporciona una forma de determinar qué extensión causó que la subida del archivo se detuviera; examinar la lista de extensiones cargadas con phpinfo() puede ayudar.'
    );

    return ( isset( $errcodes[$err] ) ) ?
        $errcodes[$err] : 'Error desconocido';
}

Tienes que verificar si la subida fue exitosa, esto podría verse así (en el método handle_upload_file())

// detenerse si algo salió mal
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', 'Subida fallida con mensaje: ' . $msg );
}

Y luego tienes que manejar el error al llamar a la clase

if ( empty( $_FILES ) ) {

global $pagenow;
$url = admin_url( $pagenow );
?>
<!-- Formulario HTML -->
<?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 {

        // hacer algo con el adjunto creado
        $attachment = get_post( $attachment_id );
        $image_url  = $attachment->guid;

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

    }
}

Recuerda siempre: ¡Nunca dejes una subida fallida sin tratar!

5 may 2013 17:58:02
Comentarios

En el formulario HTML básico del código anterior no vi un if antes de la declaración else

Monogot Monogot
5 may 2013 23:52:29
0

Añadir código en el archivo functions.php

add_action('wp_ajax_custom_action', 'custom_action');
add_action('wp_ajax_nopriv_custom_action', 'custom_action');
function custom_action() {

$post_id =12;
    //print_r($_FILES); exit;
    global $wpdb;
    //$response = array();

    if( $_FILES['uploadedfiles']) {

    $file = $_FILES['uploadedfiles'];
    require_once( ABSPATH . 'wp-admin/includes/admin.php' );
    $file_return = wp_handle_upload( $file, array('test_form' => false ) );
    if( isset( $file_return['error'] ) || isset( $file_return['upload_error_handler'] ) ) {
        return false;
    } else {
        $filename = $file_return['file'];
        $attachment = array(
            'post_mime_type' => $file_return['type'],
            'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $filename ) ),
            'post_content' => '',
            'post_status' => 'inherit',
            'guid' => $file_return['url']
        );
        $attachment_id = wp_insert_attachment( $attachment, $file_return['url'] );
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        $attachment_data = wp_generate_attachment_metadata( $attachment_id, $filename );
        wp_update_attachment_metadata( $attachment_id, $attachment_data );

        update_post_meta($post_id, '_thumbnail_id', $attachment_id);
        if( 0 < intval( $attachment_id ) ) {
          return $attachment_id;
        }
    }
    return false;

    }

    //$response['message'] = '¡Imagen subida!';
    //echo json_encode($response);
    wp_die();


}
13 feb 2020 09:40:15