Subir un archivo a un campo blob

Estuve siguiendo el tutorial de como almacenar un archivo mediante el uso de un controlador, el ejemplo guarda el archivo en una ruta del servidor y lo que yo necesitaba era subir archivos a campos BLOB en una BD.

http://www.yiiframework.com/doc/cookbook/2/

Escribo esto con la intención de que les sirva de guia al momento de subir archivos a campos BLOB en la BD.




public function actionCreate()

{

    $model=new modeloArchivo;

    if(isset($_POST['modeloArchivo']))

    {

        $model->attributes = $_POST['modeloArchivo'];

        $archivo = CUploadedFile::getInstance($model,'contenido');


        $model->nombre = $archivo->name;

	$model->extension = $archivo->type;


        // estas siguientes 3 lineas hacen al diferencia para guardar el contenido

        $fp = fopen($archivo->tempName, 'r');

        $content = fread($fp, filesize($archivo->tempName));

        fclose($fp);

	

        // esta linea almacena el contenido del archivo subido

        $model->contenido = $content;


        if($model->save())

        {

            // redirect to success page

        }

    }

    $this->render('create', array('model'=>$model));

}



En este caso mi tabla archivo contiene 3 campos que son: [nombre], [extension] y [contenido].

mira este post http://www.yiiframework.com/forum/index.php?/topic/3457-issue-with-uploading-to-database/

Vaya… Vaya… no me habia percado de que existia un metodo llamado getTempName() y que resumia mis 3 lineas para tomar el archivo del temporal y tenerlo en una variable stream…

he probado tu ejemplo pero no me funciona , no se porque y no me saca ningun error.

por que se recive por $_POST y no por $_FILES,

me podias detallar mas el archivo vista y el modelo , para ver que me falta.

gracias

yo tengo imagenes subidas al servidor para mostrarlas al usuario antes de guardarlas , utilizo este mismo codigo pero cambiando las lineas para que lea del archivo fisico y no me saca error pero tampoco sube a la tabla:





$model->attributes = $post_gestionar;

$tempName	= $val; //'../'.Yii::app()->getBaseUrl().'/images/tmp/'; //ruta fisica en el server

//$archivo = CUploadedFile::getInstance($model,'docalmacenado');

$imagen = new Thumb(); // cree una funcion para sacar thumbs de las images subidas

$imagen->loadImage($tempName); //carga la imagen y saca todas sus caracteristicas: ancho, alto, nombre




$model->file_name = $imagen->__getNombreImagen(); //obtengo el nombre de la imagen

$model->file_type = $imagen->__getTypeImagen(); //obtengo el typo de imagen




$fp = fopen($tempName, 'rb');

$content = fread($fp, filesize($tempName));

fclose($fp);


// esta linea almacena el contenido del archivo subido

$model->docalmacenado = $content;

if($model->validate()){

if($model->save()){

echo json_encode(array('mensaje'=>'ya se grabo'));// funcion que me saca los mensajes de alerta

}

}






::)

Y pusiste en el form multipart ?? porque esa es la unica razon por la cual no pase $_FILES


<?php

  echo CHtml::beginForm('', 'post', array('enctype'=>'multipart/form-data'));

?>

Define el uso de multipart como te dice Jack, pero primero debes de tener eso segundo debes de enviar por submit, he usado anteriormente el uso de CUploadFile que en la misma es como usar $_FILES dentro de PHP puro, es la parte que contiene nuestras variables temporales yo no tenia entendido mejor dicho no tenia el conocimiento de que existe una variable temporal que te asigne eso…, segunda si vas a definir una variable en tus rules dentro de tu model, y no la tienes en tu tabla donde necesitas adjuntar define esa variable temporal si es que lo quieres hacer de esta manera para eso define una propiedad en tu archivo model, eh indica el tamaño de esa propiedad que le desees dar osea si tu quieres indicar un tamaño maximo de archivos:




//mi propiedad se llama archivo

public $archivo;


array('archivo', 'FileValidator','types'=>'doc, docx, pdf, jpg','maxSize'=>10485760),


//Fijate que le defino que tipo de archivos le permito subir y ahi pongo el tamaño ahora analiza uqe estoy usando la libreria FileValidator este es un archivo que descargue no me acuerdo bien de que parte de yii.






//Código del archivo FileValidator

class FileValidator extends CFileValidator

{

	/**

	 * Internally validates a file object.

	 * @param CModel $object the object being validated

	 * @param string $attribute the attribute being validated

	 * @param CUploadedFile $file uploaded file passed to check against a set of rules

	 */

	protected function validateFile($object, $attribute, $file)

	{

		if(null===$file || ($error=$file->getError())==UPLOAD_ERR_NO_FILE)

			return $this->emptyAttribute($object, $attribute);

		else if($error==UPLOAD_ERR_INI_SIZE || $error==UPLOAD_ERR_FORM_SIZE || $this->maxSize!==null && $file->getSize()>$this->maxSize)

		{

			$message=$this->tooLarge!==null?$this->tooLarge : Yii::t('yii','The file "{file}" is too large. Its size cannot exceed {limit} MB.');

			$this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->sizeToMegaBytes($this->getSizeLimit())));

		}

		else if($error==UPLOAD_ERR_PARTIAL)

			throw new CException(Yii::t('yii','The file "{file}" was only partially uploaded.',array('{file}'=>$file->getName())));

		else if($error==UPLOAD_ERR_NO_TMP_DIR)

			throw new CException(Yii::t('yii','Missing the temporary folder to store the uploaded file "{file}".',array('{file}'=>$file->getName())));

		else if($error==UPLOAD_ERR_CANT_WRITE)

			throw new CException(Yii::t('yii','Failed to write the uploaded file "{file}" to disk.',array('{file}'=>$file->getName())));

		else if(defined('UPLOAD_ERR_EXTENSION') && $error==UPLOAD_ERR_EXTENSION)  // available for PHP 5.2.0 or above

			throw new CException(Yii::t('yii','File upload was stopped by extension.'));


		if($this->minSize!==null && $file->getSize()<$this->minSize)

		{

			$message=$this->tooSmall!==null?$this->tooSmall : Yii::t('yii','The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.');

			$this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->minSize));

		}


		if($this->types!==null)

		{

			if(is_string($this->types))

				$types=preg_split('/[\s,]+/',strtolower($this->types),-1,PREG_SPLIT_NO_EMPTY);

			else

				$types=$this->types;

			if(!in_array(strtolower($file->getExtensionName()),$types))

			{

				$message=$this->wrongType!==null?$this->wrongType : Yii::t('yii','The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.');

				$this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{extensions}'=>implode(', ',$types)));

			}

		}

	}

	

	private function sizeToMegaBytes($size)

	{

		$size = ($size/1048576*100000)/100000;

		return $size;

	}

}



y para descargar el archivo uso la extensión CDownload




/**

 * @author Rasmus Schultz

 * @link http://mindplay.dk

 * @copyright Copyright &copy; 2010 Rasmus Schultz

 * @license http://www.gnu.org/licenses/lgpl-3.0.txt

 */


/**

 * This class allows multi-threaded file downloads, and regular file 

downloads.

 *

 * You can use this class when you need to control which users are allowed 

to

 * download a file from a protected area of the local filesystem.

 *

 * Note that downloading files in this way does result in some memory and 

processor

 * overhead - you should not use this class sporadically for all downloads 

in your

 * application, only when a regular download is not possible for some 

reason (usually

 * because the file in question must not be made available to the general 

public).

 *

 * Avoid using this class when you need to log access to a public file - 

you're

 * probably better off using a pre-download logging action, which then 

redirects to

 * the actual file download.

 */

class CDownload {

  

  /**

   * Buffer size (memory requirement per download / user / thread)

   */

  const BUFFER_SIZE = 32768; # = 1024*32

  

  /**

   * Time limit (time allowed to send one buffer)

   */

  const TIME_LIMIT = 30;

  

  /**

   * Sends a file to the client.

   * 

   * This is a blocking method, which means that the method-call will not 

return until

   * the client has completed the download of the file (or segment), or 

until the client

   * is disconnected and the connection/download is aborted.

   * 

   * Check the return value of this method to see if the download was 

successful - this

   * if useful, for example, if you need to count and limit the number of 

times a user can

   * download a file; you should increase your counters only if the call 

returns TRUE.

   * 

   * It is important that your action produces <strong>no other 

output</strong> before

   * or after calling this method, as this will corrupt the downloaded 

binary file.

   * 

   * Output buffers will be cleared and disabled, and any active 

CWebLogRoute instances

   * (which might otherwise output logging information at the end of the 

request) will

   * be detected and disabled.

   * 

   * If your application might produce any other output after your action 

completes, you

   * should suppress this by using the exit statement at the end of your 

action.

   * 

   * This method throws a CException if the specified path does not point 

to a valid file.

   * 

   * This method throws a CHttpException (416) if the requested range is 

invalid.

   * 

   * @param string full path to a file on the local filesystem being sent 

to the client.

   * @param string optional, alternative filename as the client will see it 

(defaults to the local filename specified in $path)

   * @return boolean true if the download succeeded, false if the 

connection was aborted prematurely.

   */

  public static function send($path, $name=null)

  {

    // turn off output buffering

    @ob_end_clean();

    

    // disable any CWebLogRoutes to prevent them from outputting at the end of the request

    foreach (Yii::app()->log->routes as $route)

    if ($route instanceof CWebLogRoute)

      $route->enabled = false;

    

    // obtain headers:

    $envs = '';

    foreach ($_ENV as $item => $value)

    if (substr($item, 0, 5) == 'HTTP_')

      $envs .= $item.' => '.$value."\n";

    if (function_exists('apache_request_headers')) {

      $headers = apache_request_headers();

      foreach ($headers as $header => $value) {

        $envs .= "apache: $header = $value\n";

      }

    }

    

    // obtain filename, if needed:

    if (is_null($name))

      $name = basename($path);

    

    // verify path and connection status:

    if (!is_file($path) || !is_readable($path) || connection_status()!=0)

      throw new CException('CDownload::send() : unable to access local file 

"'.$path.'"');

    

    // obtain filesize:

    $size = filesize($path);

    

    // configure download range for multi-threaded / resumed downloads:

    if (isset($_ENV['HTTP_RANGE']))

    {

      list($a, $range) = explode("=", $_ENV['HTTP_RANGE']);

    }

    else if (function_exists('apache_request_headers'))

    {

      $headers = apache_request_headers();

      if (isset($headers['Range'])) {

        list($a, $range) = explode("=", $headers['Range']);

      } else {

        $range = false;

      }

    }

    else

    {

      $range = false;

    }

    

    // produce required headers for partial downloads:

    if ($range)

    {

      header('HTTP/1.1 206 Partial content');

      list($begin, $end) = explode("-", $range);

      if ($begin == '')

      {

        $begin = $size-$end;

        $end = $size-1;

      }

      else if ($end == '')

      {

        $end = $size-1;

      }

      $header = 'Content-Range: bytes '.$begin.'-'.$end.'/'.($size);

      $size = $end-$begin+1;

    }

    else

    {

      $header = false;

      $begin = 0;

      $end = $size-1;

    }

    

    // check range:

    if (($begin > $size-1) || ($end > $size-1) || ($begin > $end))

      throw new CHttpException(416,'Requested range not satisfiable');

    

    // suppress client-side caching:

    header("Cache-Control: no-store, no-cache, must-revalidate");

    header("Cache-Control: post-check=0, pre-check=0", false);

    header("Pragma: no-cache");

    header("Expires: ".gmdate("D, d M Y H:i:s", mktime(date("H")+2, 

date("i"), date("s"), date("m"), date("d"), date("Y")))." GMT");

    header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");

    

    // send a generic content-type:

    header("Content-Type: application/octet-stream");

    

    // send content-range header, if present:

    if ($header) header($header);

    

    // send content-length header:

    header("Content-Length: ".$size);

    

    // send content-disposition, with special handling for IE:

    if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE)

    {

      header("Content-Disposition: inline; filename=".str_replace(' ', 

'%20', $name));

    }

    else

    {

      header("Content-Disposition: inline; filename=\"$name\"");

    }

    

    // set encoding:

    header("Content-Transfer-Encoding: binary\n");

    

    // stream out the binary data:

    if ($file = fopen($path, 'rb'))

    {

      fseek($file, $begin);

      $sent = 0;

      while ($sent < $size)

      {

        set_time_limit(self::TIME_LIMIT);

        $bytes = $end - ftell($file) + 1;

        if ($bytes > self::BUFFER_SIZE)

          $bytes = self::BUFFER_SIZE;

        echo fread($file, $bytes);

        $sent += $bytes;

        flush();

        if (connection_aborted())

          break;

      }

      fclose($file);

    }

    

    // check connection status and return:

    $status = (connection_status()==0) and !connection_aborted();

    return $status;

  }

  

}



Ojala te sirva nada mas faltaria indicar tus botones que lleguen al metodo donde vas a guardar y ese código es como el de Jack Fiallos. Saludos.

Se me olvido definirte que debes de indicar una propiedad o ponle el texto en una variable que desees, para saber en que carpeta vas a crear tu archivo mejor dicho como se llamara tu carpeta donde lo crearas, y la otra debes de crear la carpeta con permisos 777 para que te aparezca fisicamente y ya de ahi para uqe lo puedas descargar seria lo unico que se me olvido explicarte. Saludos.

Oye amigo, estoy tratando de aplicar tu ejemplo pero este código que pones arriba dónde lo encuentro porque en el _form donde capturo mis datos no está!..

mi form es este:




<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'archivo-form',

	'enableAjaxValidation'=>false,

)); ?>


	<p class="note">Fields with <span class="required">*</span> are required.</p>


	<?php echo $form->errorSummary($model); ?>


	<div class="row">

		<?php echo $form->labelEx($model,'nombre'); ?>

		<?php echo $form->textField($model,'nombre',array('size'=>40,'maxlength'=>40)); ?>

		<?php echo $form->error($model,'nombre'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'extension'); ?>

		<?php echo $form->textField($model,'extension',array('size'=>40,'maxlength'=>40)); ?>

		<?php echo $form->error($model,'extension'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'contenido'); ?>

		<?php echo $form->fileField($model,'contenido'); ?>

		<?php echo $form->error($model,'contenido'); ?>

	</div>


	<div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

	</div>


<?php $this->endWidget(); ?>


</div><!-- form -->



mmmm no me habia molestado en buscar una extension para hacer esto, a ver si la necesito en un proyecto.

Gracias ^^

no, esa linea no está, debes ponerla tu e iria algo asi:


<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'file-form',

	'enableAjaxValidation'=>false,

	'htmlOptions' => array('enctype' => 'multipart/form-data'),

)); ?>

ademas hay varios campos en ese formulario que no debes usar (en realidad solo necesitas el input para seleccionar el archivo), los datos que no se piden en el formulario salen del archivo.

en el otro hilo en mi ultima respuesta adjunte un ejemplo de como puedes hacer lo que necesitas