viernes, 19 de octubre de 2012

File Uploads en Yii Framework (con "drag and drop")



A continuación presentaré tres alternativas para subir archivos a tu aplicación web: Basica,  Coco y YiiFileManagerFilePicker , ambas tienen sus propios beneficios y sus diferencias. 

Adicionalmente me gustaría presentar el artículo en donde muestro cómo recibir archivos por una vía remota (desde otra aplicación), está relacionado con la subida de archivos porque las personas siempre piensan que la única vía de subir archivos es mediante un widget y la realidad muestra que tu aplicación pudiera proveer un mecanismo para recibir archivos por un canal http.


La manera estandar de subir archivos en Yii Framework

Necesitas leer acerca de estas dos clase: CUploadedFile  y CFileValidator,  la primera es para manejar el archivo subido, y la segunda es para validar el archivo que esta siendo subido.  CUploadedFile se encarga de lidiar con el estandar $_FILES de php.

Paso 1, El modelo para subir un archivo:


2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// protected/models/ImportEmployeesForm.php
class ImportEmployeesForm extends CFormModel {
    public $the_file;
    public function rules()
    {
        return array(
            array('the_file', 'file', 'allowEmpty'=>false,
                'types'=>'csv',
                'maxSize'=>array(1024 * 2000),
                'message'=>'Solo se admiten archivos de texto con extensión CSV'),
        );
    }
    public function attributeLabels(){
        return array(
            "the_file"=>"Archivo a Importar",
        );
    }
}



Paso 2, La vista que aloja al formulario:




<?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(
    'id'=>'importemployees-form',
    'enableAjaxValidation'=>false,
    'type'=>'horizontal',
    'htmlOptions'=>array("enctype"=>"multipart/form-data"),
)); ?>
    <div class='file-uploader'>
        <?php echo $form->fileFieldRow($model,'the_file'); ?>
    </div>
    <div class='buttons'>
        <?php
        $this->widget('bootstrap.widgets.TbButton', array(
            'buttonType'=>'submit',
            'label'=>'Subir Archivo',
            'type'=>'primary', // null, 'primary', 'info', 'success', 'warning', 'danger' or 'inverse'
            'size'=>'large', // null, 'large', 'small' or 'mini'
            'htmlOptions'=>array(
            ),
        ));
        ?>
    </div>
<?php $this->endWidget(); ?>

Paso 3, La controladora que usa al modelo y la vista para descargar y procesar el archivo subido.

public function actionImportEmployees(){
   $model = new ImportEmployeesForm();
   if(isset($_POST["ImportEmployeesForm"])){
       $model->attributes = $_POST["ImportEmployeesForm"];
       $model->the_file = CUploadedFile::getInstance($model, "the_file");
       if($model->validate()){ // aqui entra CFileValidator
          $filename = $model->the_file->tempName;
          //hacer algo con el archivo recien subido...
       }
    }
    $this->render("importemployees",array('model'=>$model));
}

A continuación otras opciones mas avanzadas:

Coco
Es un widget para implementar en tu formulario que mostrará un multi-file-uploader ajax-based, para recibir los archivos subidos deberás indicarle al widget la ubicación de un método en alguna clase el cual recibirá los archivos. En el caso particular de coco la funcionalidad es heredada de un componente preexistente fabricado por valums/fileuploader.



el widget coco ofrece el boton "find & upload", la lista de archivos bajo el botón aparece cuando se han ido subiendo archivos. permite drag&drop.



YiiFileManagerFilePicker
Es mas avanzado que Coco, también es un multi-file-uploader, también ofrece un widget al igual que Coco, pero agrega mas funcionalidad y extensibilidad, por ejemplo:

1. El usuario tiene un explorador de sus archivos subidos con capacidad de renombrar archivos, eliminarlos, seleccionar varios.


2. La presentación del widget depende de tu diseño, tu fabricas el layout del widget incrustándole las piezas html que el widget pide pero con el estilo y ubicación que tu necesitas.

3. El control del widget se hace desde una sub clase que alojas en tus componentes, donde harás los cambios de código necesarios sin alterar al core del widget.

4. Gracias al subclassing (lee el punto 3) puedes oir eventos, controlar mejor qué ha subido el usuario, qué quiere subir, tipos de archivos admitidos, tamaños, mimetypes y muy importante: control basado en el estado del usuario.

5. Permite aplicar visores para los distintos tipos de archivo, pudiendo tu exportar una URL de algún archivo de usuario para ser visto desde fuera de tu aplicación.

 
Aquí el widget es presentado en modo embedido (como parte de la página) pero puede presentarse también en modo "dialog box" (como una ventana flotante que aparece cuando se requiera)



USANDO COCO FILE UPLOADER

Ir al sitio web de Coco


Se hace en dos partes:

Primero: Pones el widget en la vista o formulario en donde lo requieras.
Segundo: en algun controller pones un action fijo en cualquier controller.

  1. DESCARGA O CLONA COCO.

    Si no usas GIT simplemente copia el contenido de la extension directamente dentro de 'extensions', si usas GIT haz lo siguiente:

    cd /home/blabla/myapp/protected
    mkdir extensions
    cd extensions
    git clone https://bitbucket.org/christiansalazarh/coco.git
  2. SETUP EN CONFIG/MAIN

    Edita tu archivo /protected/config/main.php
    'import'=>array(
            'application.models.*',
            'application.components.*',
            'application.extensions.coco.*',            // <------agrega esto
        ),

  3. CONECTA A "COCO" A TU APLICACION CON UN ACTION ESTATICO

    Edita protected/controllers/siteController.php (aunque puedes usar otra).
    Este action solo es requerido una vez para todo el proyecto !!
    y agregale lo siguiente (coloreado) :

    public function actions()
        {
            return array(
                'captcha'=>array(
                    'class'=>'CCaptchaAction',
                    'backColor'=>0xFFFFFF,
                ),
                'page'=>array(
                    'class'=>'CViewAction',
                ),
                'coco'=>array(
                    'class'=>'CocoAction',
                ),
            );
        }
  4. INSERTA EL WIDGET EN UNA VISTA

    widget('ext.coco.CocoWidget'
            ,array(
                'id'=>'cocowidget1',
                'onCompleted'=>'function(id,filename,jsoninfo){  }',
                'onCancelled'=>'function(id,filename){ alert("cancelled"); }',
                'onMessage'=>'function(m){ alert(m); }',
                'allowedExtensions'=>array('jpeg','jpg','gif','png'),
                'sizeLimit'=>2000000,
                'uploadDir' => 'assets/',
                // para recibir el archivo subido:
                'receptorClassName'=>'application.models.MyModel',
                'methodName'=>'onFileUploaded',
                'userdata'=>$model->primaryKey,
            ));
       ?>
    

¿ CÓMO FUNCIONA COCO ?
  1. Cuando alguien visite la vista en donde insertaste el Widget verás que aparece un botón con el texto que pusiste en el argumento "buttonText", el cual por defecto dice: "Find & Upload".
  2. Alguien podrá arrastrar un archivo a ese botón o podrá darle clic y éste le presentará una caja de selección de archivo a subir.
  3. El usuario envia el archivo, y coco internamente invoca a tu action: index.php?r=site/coco con algunos argumentos. Va a transferir a ese action el archivo a subir mediante llamadas ajax.  Tu no haces nada en este punto, solo mirar como coco lo hace, incluso te presentará una caja de progreso cancelable.
  4. Cuando el action determina que el archivo fue subido (lo sube en la carpeta que tu indicas en el atributo "uploadDir") entonces, hara una de estas dos cosas:

    a) Si no has configurado los argumentos: receptorClassName y methodName entonces Coco dejará el archivo en el directorio assets (o donde tu digas en "uploadDir") y solo recibiras una notificacion via ajax en el metodo javascript que has definido en "onCompleted".

    b) Si has configurado los argumentos: receptorClassName y methodName, entonces coco desde el lado del servidor creara una instancia de la clase que tu configures en "receptorClassName" y luego invocará un método: aquel que pusiste en "methodName".  En ese método podrás recibir la notificacion del archivo subido.

    Por tanto, para que esta opción B funcione deberás crear una clase asi:

    // creas la clase en: protected/models/MyModel.php  (o donde quieras)
    class MyModel {

        public function onFileUploaded($fullFileName,$userdata) {
            // userdata es el mismo valor que pusiste en config/main
            // fullFileName es la ruta del archivo listo para leer.
        }
    }

    y los argumentos a pasarle al widget serían:
                'receptorClassName'=>'application.models.MyModel',
                'methodName'=>'onFileUploaded',


OTRAS OPCIONES DEL WIDGET:

'buttonText'=>'Find & Upload',
'dropFilesText'=>'Drop Files Here !',
'htmlOptions'=>array('style'=>'width: 300px;'),
'defaultControllerName'=>'site',
'defaultActionName'=>'coco',


USANDO YIIFILEMANAGERFILEPICKER FILE UPLOADER & FILE EXPLORER

Ir al sitio web de YiiFileManagerFilePicker

dependencia:  la extensión YiiFileManager  (es muy simple de instalar)

1. puedes descargar la extensión para Yii Framework o clonarla de aquí:

cd yourapp/protected/extensions/
git clone https://bitbucket.org/christiansalazarh/yiifilemanagerfilepicker.git

2. En tu archivo protected/config/main.php agregarás:

'import'=>array(
    'application.models.*',
    'application.components.*',
    'application.extensions.yiifilemanager.*',
    'application.extensions.yiifilemanagerfilepicker.*', // <<--THIS
), 

3. En algún controller (por defecto siteController) debes crear un action que implemente la clase YiiFileManagerFilePickerAction como indico aca, es para que el widget pueda comunicarse con la aplicación.

class SiteController extends Controller {
    public function actions()
    {
    return array(
        'captcha'=>array(
        'class'=>'CCaptchaAction','backColor'=>0xFFFFFF,),
        'page'=>array('class'=>'CViewAction',),
        'yiifilemanagerfilepicker'=>array(
    'class'=>
        'ext.yiifilemanagerfilepicker.YiiFileManagerFilePickerAction'),
    );
    }
...
}
 
4. Debes crear la clase que dará vida al widget. Creala dentro de tus componentes, para facilitar las cosas ya hay una clase lista para ser copiada y usada como patrón, por favor no uses la clase directamente desde la extensión, haz una copia en tus componentes. Los detalles de la clase los puedes conseguir en el instructivo README.md dentro de la extensión.


#copiala desde aqui:
'protected/extensions/yiifilemanagerfilepicker/demo-component/MyYiiFileManViewer.php'

#a tu propia aplicacion:
'protected/components/MyYiiFileManViewer.php'



5. El widget requiere algunos iconos, la extensión trae algunos predefinidos:

# copiar los iconos a tu aplicacion desde aqui:
'yourapp/protected/extensions/yiifilemanagerfilepicker/demo-images'  

# a esta ubicacion:
'tuaplicacion/images/'

6. En alguna vista insertas el codigo HTML que el widget usará para presentar en el la información, aqui puedes dar el formato html requerido, clases CSS etc, pero respetando lo que aquí se presenta:

<div>Select a Background image: <a href='#' id='file-picker'>click here</a>
    <img src='' width='50%' id='selected-image' />
</div>
<div id='file-picker-viewer'>
    <div class='body'></div>
    <hr/>
    <div id='myuploader'>
        <label rel='pin'><b>Upload Files
            <img style='float: left;' src='images/pin.png'></b></label>
        <br/>
        <div class='files'></div>
        <div class='progressbar'>
            <div style='float: left;'>
                Uploading your file(s), please wait...</div>
            <img style='float: left;' src='images/progressbar.gif' />
            <div style=
                'float: left; margin-right:10px;'class='progress'>
            </div>
            <img style='float: left;' class='canceljob' 
                src='images/delete.png' title='cancel the upload'/>
        </div>
    </div>
    <hr/>
    <button id='select_file' class='ok_button'>Select File(s)</button>
    <button id='delete_file' class='delete_button'>
        Delete Selected File(s)</button>
    <button id='close_window' class='cancel_button'>Close Window
        </button>
</div>
<hr/>Logger:<br/><div id='logger'></div>

7. En la misma vista anterior (paso 6) insertas el widget. 
Cuando el usuario hace click en el elemento html #file-picker (en el paso6) entonces aparecerá un file-chooser











Este file-chooser (imagen arriba), aparecera cuando se haga click en el DIV insertado en el paso 6. Para que eso ocurra debes insertar este widget en la misma vista donde pusiste el código del paso6.

Por defecto en este caso el file-chooser aparece en forma embedida, es decir, sin mostrar un dialogbox, para hacerlo con un dialogbox se requiere un minimo de cambios expuestos en el README.md de la extensión (en ingles).



<?php
    // the widget
    //
    $this->widget('application.components.MyYiiFileManViewer'
    ,array(
        // layout selectors:
        'launch_selector'=>'#file-picker',
        'list_selector'=>'#file-picker-viewer',
        'uploader_selector' => '#myuploader',
        // messages:
        'delete_confirm_message' => 'Confirm deletion ?',
        'select_confirm_message' => 'Confirm selected items ?',
        'no_selection_message' => 'You are required to select some file',
        // events:
        'onBeforeAction'=>
            "function(viewer,action,file_ids) { return true; }",
        'onAfterAction'=>
            "function(viewer,action,file_ids, ok, response) { 
                if(action == 'select'){ 
                  // actions: select | delete
                  $.each(file_ids, function(i, item){ 
                  $('#logger').append('file_id='+item.file_id 
                  + ', <img src=\''+item.url+'&size=full\'><br/>');
                });
            }
        }",
        // 'onBeforeLaunch'=>"function(_viewer){ }",
        'onClientSideUploaderError'=>
            "function(messages){ 
                $(messages).each(function(i,m){  alert(m); }); 
            }
        ",
        'onClientUploaderProgress'=>"function(status, progress){
            $('#logger').append(
                'progress: '+status+' '+progress+'%<br/>');
            }",
        ));
?>

Basicamente el widget solo te pide los nombres de los componentes html donde se presentará al file-chooser además de las funciones de escucha de eventos.
 
Lo que hace este widget es insertar eventos jQUERY para que cuando hagas click en el lanzador del paso6 (#file-picker) entonces aparezca un selector de archivos (imagen arriba de esta nota).  

La manera en cómo tu quieres presentar el lanzador de archivos es tu asunto, el widget se enfoca en proveer funcionalidad y tu responsabilidad es la de decir cómo se presenta.

Hay algunos eventos javascript que te gustaría oir, a los cuales puedes adosar funciones JS:

onBeforeAction  #lanzado antes que se seleccionen los archivos
onAfterAction  #lanzado cuando se ha hecho click en seleccionar archivos 
onClientSideUploaderError #cuando en el browser ha ocurrido un problema
onClientUploaderProgress  #para ver el progreso de la subida
¿ Dónde controlas los tipos de archivo requerido y demás asuntos ? en tu clase 'protected/components/MyYiiFileManViewer.php', la cual hiciste en el paso4.

8. Y cómo funciona ?

Recuerdas en el paso6 este extracto de código ?
<div>Select a Background image: <a href='#' id='file-picker'>click here</a>
    <img src='' width='50%' id='selected-image' />
</div>
Eso presentará un link asi: "Select a background image: click here"

Cuando el usuario hace click entonces via jQUERY aparecerá el file-chooser apuntado por el selector jQUERY: "#file-picker" en el cual tu tienes definido el layout html a su vez renderizado por el widget del paso7.

Cuando el usuario hace selección de archivos en el file-chooser tu recibes eventos jquery, cuando ha seleccionado archivos y estos son subidos al servidor tu recibes eventos en la clase del paso4 ('protected/components/MyYiiFileManViewer.php') en donde tu aceptas y controlas.

El usuario puede guardar archivos en su propio espacio web, esto es controlado por otro componente que no he nombrado pero que forma parte de las dependencias de YiiFileManagerFilePicker: 
la extensión YiiFileManager  esta es muy simple de instalar y se encarga de darle al widget los archivos del usuario.

De nuevo, en la clase 'protected/components/MyYiiFileManViewer.php'
es donde tu manejas los archivos seleccionados o subidos.  Recuerda que el usuario puede seleccionar un archivo que previamente habia subido en otra sesión.


Links usados en este post

Cómo recibir archivos via http 

YiiFileManager (low level file manager yiiframework)

YiiFileManagerFilePicker (ajax based file uploader & file explorer yiiframework)

YiiFileManagerRemote

Coco (ajax based file uploader yiiframework)

38 comentarios:

  1. un favor tengo un error me sale lo siguiente

    include(CJavaScriptExpression.php) [function.include]: failed to open stream: No such file or directory

    no se a que se debe te agradesco de antemano

    ResponderEliminar
    Respuestas
    1. https://bitbucket.org/christiansalazarh/coco/issue/2/include-cjavascriptexpressionphp

      Eliminar
  2. hola. eso es porque tu versión de yii framework es muy baja. actualizala. CJavaScriptExpression es usada para mapear eventos que serán invocados por javascript.

    ResponderEliminar
    Respuestas
    1. Buenisimo!!! ya funciona solo que al subir el archivo sale error estoy revisandotu pagina para encontrar cual el problema

      Eliminar
    2. ahora me sale el error failed despues de elegir el archivo intenta subirlo pero al final sale un error diciendo "Failed" probe con diferentes tamaños de archivos creyendo que era por el tamaño en todos me sale ese error.

      y una consulta una ves que se sube el archivo se guarda en alguna variale la direccion? ya que me serviria para almacenarlo en mi BD

      gracias por tu colaboracion

      Eliminar
    3. hola en la doc o en la lista de issues puedes ayudarte mejor, aunque te adelanto que eso normalmente ocurre por dos cosas:

      1. permisos insuficientes en el directorio de uploads, que se resuelven de primera mano con un chmod 777.

      2. el argumento sizeLimit del widget tiene un valor mayor que el definido en php.ini (max_upload_size).

      Eliminar
    4. Mira intente los dos metodos que dijiste en el primero use attrib ya que uso Windows en el tema de sizeLimit ya modifique en varias instancias y no resulto. Es mas intente cambiar de extension por eajaxupload y al final tengo el mismo error de Failed ya estoy super estresado que no sale una ayuda clamorosa

      gracias

      Eliminar
    5. hola raul.

      ya revisaste el log de coco ? que dice ?

      Eliminar
    6. Christian

      imagino que te refieres al archivo access.log

      encontre lo siguiente:

      127.0.0.1 - - [10/Nov/2012:19:35:21 -0400] "POST /proyecto/SiteController/coco&action=upload&nocache=1352590514170&data=a:6:%7Bs:17:%22allowedExtensions%22;a:4:%7Bi:0;s:4:%22jpeg%22;i:1;s:3:%22jpg%22;i:2;s:3:%22gif%22;i:3;s:3:%22png%22;%7Ds:9:%22sizeLimit%22;i:200000;s:9:%22uploadDir%22;s:7:%22assets/%22;s:17:%22receptorClassName%22;s:26:%22application.models.MyModel%22;s:10:%22methodName%22;s:14:%22onFileUploaded%22;s:8:%22userdata%22;N;%7D?qqfile=Desert.jpg HTTP/1.1" 404 364 "http://localhost/proyecto/documentos/create" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0"

      Eliminar
    7. no, me refiero al archivo de log de Yii framework. esta ahi anotado en la documentacion de coco.

      este archivo se halla en:
      /tu/app/protected/runtime/application.log

      coco informará bajo el key "info" o "error", ambos se deben habilitar en la seccion de log de tu config/main.php sino no verás el registro de error.

      Eliminar
    8. revise el archivo y el ultimo registro en ese archivo dice:

      2012/11/12 11:42:28 [error] [exception.CHttpException.404] exception 'CHttpException' with message 'No es posible resolver la solicitud "SiteController/coco&action=upload&nocache=1352734938165&data=a:6:{s:17:"allowedExtensions";a:4:{i:0;s:4:"jpeg";i:1;s:3:"jpg";i:2;s:3:"gif";i:3;s:3:"png";}s:9:"sizeLimit";i:200000;s:9:"uploadDir";s:7:"assets/";s:17:"receptorClassName";s:26:"application.models.MyModel";s:10:"methodName";s:14:"onFileUploaded";s:8:"userdata";N;}"' in C:\xampplite\htdocs\yii\framework\web\CWebApplication.php:287

      Stack trace:

      #0 C:\xampplite\htdocs\yii\framework\web\CWebApplication.php(142): CWebApplication->runController('SiteController/...')

      #1 C:\xampplite\htdocs\yii\framework\base\CApplication.php(162): CWebApplication->processRequest()

      #2 C:\xampplite\htdocs\proyecto\index.php(13): CApplication->run()

      #3 {main}
      REQUEST_URI=/proyecto/SiteController/coco&action=upload&nocache=1352734938165&data=a:6:%7Bs:17:%22allowedExtensions%22;a:4:%7Bi:0;s:4:%22jpeg%22;i:1;s:3:%22jpg%22;i:2;s:3:%22gif%22;i:3;s:3:%22png%22;%7Ds:9:%22sizeLimit%22;i:200000;s:9:%22uploadDir%22;s:7:%22assets/%22;s:17:%22receptorClassName%22;s:26:%22application.models.MyModel%22;s:10:%22methodName%22;s:14:%22onFileUploaded%22;s:8:%22userdata%22;N;%7D?qqfile=Desert.jpg

      HTTP_REFERER=http://localhost/proyecto/documentos/create
      ---

      Eliminar
    9. hola un 404 es un indicador de "page not found". algo malo tienes en tu codigo respecto a las URL. Muestrame cómo configuraste a coco en config/main y en El widget como tal. En ambas, no me mandes solo una

      Eliminar
    10. ok esta es mi vista con el wiget:


      renderPartial('_form', array('model'=>$model)); ?>

      widget('ext.coco.CocoWidget'
      ,array(
      'id'=>'cocowidget1',
      'onCompleted'=>'function(id,filename,jsoninfo){ }',
      'onCancelled'=>'function(id,filename){ alert("cancelled"); }',
      'onMessage'=>'function(m){ alert(m); }',
      'allowedExtensions'=>array('jpeg','jpg','gif','png'),
      'sizeLimit'=>200000,
      'uploadDir' => 'assets/', // coco will @mkdir it
      // this arguments are used to send a notification
      // on a specific class when a new file is uploaded,
      ... 'receptorClassName'=>'application.models.MyModel',
      'methodName'=>'onFileUploaded',
      'userdata'=>$model->primaryKey,
      ));
      ?>
      ....
      y el este parte del codigo de main
      ....
      'sourceLanguage'=>'en',
      'charset'=>'iso-8859-1',
      'preload'=>array('log'),
      'import'=>array(
      'application.models.*',
      'application.components.*',
      'application.extensions.coco.*',
      ),
      'modules'=>array(
      'gii'=>array(
      'class'=>'system.gii.GiiModule',
      'password'=>'prietor',

      ....

      Eliminar
    11. y la conexión de coco al site controller la pusiste ? el widget esta bien configurado segun me muestras, pero si éste no está conectado al sitecontroller (u otro controller) no va a funcionar.

      Eliminar
    12. igualmente agrego, porqué dice
      "... 'receptorClassName'=>'application.models.MyModel',"

      y no: (sin ...)
      'receptorClassName'=>'application.models.MyModel',

      tu clase MyModel existe ? y el metodo onFileUploaded existe en esa clase ?

      Eliminar
    13. el SiteController esta configurado coloque el codigo que se describe

      'page'=>array(
      'class'=>'CViewAction',
      ),
      'coco'=>array(
      'class'=>'CocoAction',
      ),


      La clase MyModel Existe, tiene el metodo onFileUploaded


      <?php

      class MyModel {

      public function onFileUploaded($fullFileName,$userdata) {

      }
      }


      ahora lo comente y tampoco funciona

      Eliminar
    14. 1. igualmente, asegurate que no hayas cambiado a defaultControllerName, si vas a pones a coco en un controller distinto, solo pones ahi el controllerName, es decir "site" no "siteController".

      2. limpia los assets. (no los de coco dentro de la extension), la ruta de los assets es: /tuapp/assets, no borres el directorio, borra su contenido y vuelve a iniciar la aplicación.

      Eliminar
    15. volvi a recrear tu caso, y efectivamente el error da si tu has modificado el valor de defaultControllerName por "siteController" en vez del valor por defecto "site".

      Debido a que en la config que me muestras no aparece tal modificación, asumo que hiciste modificaciones dentro del codigo fuente de "coco".

      Eliminar
    16. si efectivamente Chistian modifique el codigo, volvi a implementar todo de cero en una nuevo proyecto subio sin problemas, ya lo adaptae a mi proyecto original y fubciona de 10 adicione otras extensiones que son las que necesito y funciona

      te agradezco de mil por el tiempo que dedicaste a resolver mi problema ahora puedo avanzar con mi proyecto gracias de nuevo!!!!!

      Eliminar
    17. que bueno que ya resolviste, solo debiste haber puesto $defaultControllerName = 'site'; como viene de fabrica y listo debio funcionar.

      Eliminar
  3. Hola, ademas de subir el archivo debo guardar la ruta en base de datos,¿Como lo puedo hacer?

    ResponderEliminar
    Respuestas
    1. hola, bueno eso se sale un poco de este tutorial, pero te informo de todos modos a modo de guia, para que tengas mayor información te invito a visitar:
      http://facebook.com/Yiiframeworkenespanol
      y
      http://www.yiiframeworkenespanol.org

      como habrás visto, coco pide un metodo en una clase
      cualquiera, ese metodo sera invocado con la
      ruta del archivo recientemente cargado.

      public function onFileUploaded($fullFileName,$userdata)
      {
      // fullFileName es la ruta del archivo
      // listo para leer.

      podrias hacer:
      $userid = Yii::app()->user->id;
      o
      $userid = $userdata;
      siendo este ultimo algun ID pasado al widget
      en la vista en donde lo pusiste. supon que es
      el ID del usuario activo que tu tienes.

      bueno, simple:
      $nuevaRuta = crea_nueva_ruta($userid);
      mover_archivo($fullFileName, $nuevaRuta);

      $usuario = Tuusuario::model()->findByPk($userid);

      // podrias usar esa foto como perfil:
      $usuario->fotoPerfil = $nuevaRuta;

      // o podrias almacenar esa foto o archivo
      // en su lista de archivos...
      $almacen = $usuario->getAlmacen();
      $almacen->agregar($nuevaRuta);
      }



      Eliminar
    2. Christiam dos dudas habe si puedes darme luz.

      Hago esto para cambiar el nombre pero no me hace nada:
      $userid = Yii::app()->user->id;
      $nuevaruta = '/doc/'.$userid;
      move_uploaded_file ($fullFileName , $nuevaruta);

      Lo otro cuando subo un archivo no lo sobrescribe, es lo correcto, le agrega un aleatorio de dos numeros, pero me regresa es el nombre del archivo sin esos dos aleatorios, como puedo hacer para tomar el nombre real?

      Muchas gracias

      Eliminar
  4. este tema de obtener la ruta como se logra?, tengo implementado el modelo MyModel.php

    <?php
    class MyModel {
    public function onFileUploaded($fullFileName,$userdata) {
    }
    }

    la idea es que pueda guardar en un registro(ubicacion) en mi BD la ruta del archivo que acabo de subir quedando asi:
    assets/prueba.xls

    ResponderEliminar
  5. Excelente trabajo. Una pregunta que widget compatible con este para la crop de imagenes.. Yo retorno la vista previa de la imagen subida pero no veo como integrarlo con algo como jiicrop para el cortado. Pistas quiza muchas gracias de nuevo por tu ayuda con Upload y esperando si alguien sabe de lo otro

    ResponderEliminar
    Respuestas
    1. hola, en el metodo onFileUploaded podrias tomar la imagen y convertirla a una version reducida usando esto:
      http://pastebin.com/REixhKaB

      Eliminar
  6. hola, en el metodo onFileUploaded podrias tomar la imagen y convertirla a una version reducida usando esto:
    http://pastebin.com/REixhKaB

    ResponderEliminar
  7. Christian como estas? logre hacer funcionar coco pero no funciona cuando lo ejecuto en una ventana modal cjuidialog, alguna solución?

    ResponderEliminar
    Respuestas
    1. Hola, aqui tienes un ejemplo, no va mas alla de insertar el CJui y listo funciona,

      pageTitle=Yii::app()->name;
      ?>

      beginWidget('zii.widgets.jui.CJuiDialog',array(
      'id'=>'mydialog',
      'options'=>array(
      'title'=>'Dialog box 1',
      'autoOpen'=>false,
      ),
      ));
      ?>

      widget('ext.coco.CocoWidget'
      ,array(
      'id'=>'cocowidget1',
      'onCompleted'=>'function(id,filename,jsoninfo){ }',
      'onCancelled'=>'function(id,filename){ alert("cancelled"); }',
      'onMessage'=>'function(m){ alert(m); }',
      'allowedExtensions'=>array('jpeg','jpg','gif','png'),
      //'sizeLimit'=>2000000,
      'uploadDir' => 'assets/', // coco will @mkdir it
      // this arguments are used to send a notification
      // on a specific class when a new file is uploaded,
      'receptorClassName'=>'application.models.ContactForm',
      'methodName'=>'onFileUploaded',
      'userdata'=>'',
      'multipleFileSelection'=>true,
      'maxConnections'=>3,
      'maxUploads'=>2,
      'maxUploadsReachMessage'=>'No puede subir mas archivos',
      ));
      ?>

      endWidget('zii.widgets.jui.CJuiDialog');
      echo CHtml::link('open dialog', '#', array(
      'onclick'=>'$("#mydialog").dialog("open"); return false;',
      ));
      ?>

      Eliminar
  8. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  9. Hola Christian, excelente trabajo, te quería preguntar, puedo limitar el numero de archivos subidos?

    ResponderEliminar
    Respuestas
    1. hola, claro que si.
      http://www.yiiframework.com/extension/coco/
      lee en esa URL sobre:
      maxUploads, sizeLimit, allowedExtensions (verifica mimetypes, no extensiones)

      Eliminar
  10. Hola Christian, sabes segui tu tuto al p[ie de la letra pero quisiera saber si me puedes ayudar... al momento de cargar o arrastrar un archivo siempre ,e dice failed....... esto me aparece en mi log ... gracias de antemano

    2013/07/17 21:52:10 [error] [exception.CHttpException.404] exception 'CHttpException' with message 'Unable to resolve the request "venta/site/coco".' in /Applications/XAMPP/xamppfiles/htdocs/Yii/framework/web/CWebApplication.php:286
    Stack trace:
    #0 /Applications/XAMPP/xamppfiles/htdocs/Yii/framework/web/CWebApplication.php(141): CWebApplication->runController('venta/site/coco')
    #1 /Applications/XAMPP/xamppfiles/htdocs/Yii/framework/base/CApplication.php(169): CWebApplication->processRequest()
    #2 /Applications/XAMPP/xamppfiles/htdocs/erpv2/index.php(13): CApplication->run()
    #3 {main}
    REQUEST_URI=/erpv2/index.php?r=venta/site/coco&action=upload&data=a%3A6%3A%7Bs%3A17%3A%22allowedExtensions%22%3Ba%3A4%3A%7Bi%3A0%3Bs%3A4%3A%22jpeg%22%3Bi%3A1%3Bs%3A3%3A%22jpg%22%3Bi%3A2%3Bs%3A3%3A%22gif%22%3Bi%3A3%3Bs%3A3%3A%22png%22%3B%7Ds%3A9%3A%22sizeLimit%22%3Bi%3A2000000%3Bs%3A9%3A%22uploadDir%22%3Bs%3A7%3A%22assets%2F%22%3Bs%3A17%3A%22receptorClassName%22%3Bs%3A26%3A%22application.models.MyModel%22%3Bs%3A10%3A%22methodName%22%3Bs%3A14%3A%22onFileUploaded%22%3Bs%3A8%3A%22userdata%22%3BN%3B%7D&qqfile=966.jpg
    HTTP_REFERER=http://localhost/erpv2/index.php?r=venta/cliente/Alta
    ---

    ResponderEliminar
  11. quisiera saber como implementar un botón de eliminar el archivo una vez subido.

    desde ya gracias.

    ResponderEliminar
  12. Hola me gustaría saber como implementarle una opcion de elegir el tipo de fichero.
    Sería un Select al lado del boton ajax donde se eligiese la opcion deseada.

    ResponderEliminar
  13. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  14. Hola Christian buen dia

    Estoy usando Coco, me anduvo bien, le agregue el ID para q me cree una carpeta y las imagenes se suban ahi, lo voy a utilizar para subir certificadosmedicos .... 'uploadDir' => 'certificados/'.$model->codigo.'/', entonces me crea en la carpeta certificados una carpeta con el ID y me guarda adentro todos los archivos q suba. para leerlo le hice un rustico link a la carpeta ...
    echo CHtml::link('Ver Certificados','certificados/'.$model->codigo.'/',); pero no es lo que quiero, me gustaria q cuando entren al linlk se vean iconos con el nombre de los archivos... con que podria implementar eso? gracias

    ResponderEliminar
    Respuestas
    1. hola YiiFileManagerFilePicker. en este mismo post el cual he reeditado.

      Eliminar