Jquery File Upload (Blueimp)

Olá a todos,

Acho que este problema ou pela estrutura do plugin, não permite enviar as imagens na submissão do formulário.

No cenário update o plugin funciona normal, por que eu tenho um ID do Model, assim eu consigo vincular a imagem ao ID do Model.

E no cenário create/insert? Não posso enviar a imagem primeiro depois o formulário, senão as imagens fica sem referência, abaixo tem algumas resposta das perguntas que irão aparecer.

Usa o formData!

Problema 1

Impossível, se eu submeter 1 arquivo por vez, cada envio da imagem, ele vai enviar os dados do formulário.

Problema 2

Se eu setar para enviar todos os arquivos de 1x, funciona, mais terei problema de timeout, imagina se o cliente seleciona 20 imagens de aproximadamente 800kb ou 1MB que seja.

Problema 3

Tentei criar um <input type="file"/> no template, mais sem chances de pegar o objeto da imagem.

Problema 4

Usei todos os callbacks possível do plugin, e sem sucesso.


O mais próximo que cheguei, foi sentando replaceFileInput para false, porém ele submete somente o <input> onde está sendo selecionado as imagens. No fileupload.js se setado como replaceFileInput: true, ele cria um clone input, mais esse clone eu não tenho acesso.

Alguem que esteja usando esse plugin, tem alguma solução para o cenário insert/create, não to querendo fazer uma mega adaptação de submeter o form em ajax, receber o ID, e depois enviar as imagens ou até mesmo enviar o form e voltar para ele, para inserir a imagem, acho que o cliente tem o privilegio de enviar tudo de uma vez.

Já tentei inúmeros plugin de envio de arquivo. O mais próximo que chega é foi o: http://www.fusionbox.org/jquery-multifile/

Mais ja alterei o core dele, ele não consegue separar cada arquivo em 1 <input>.

Pra quem é persistente sempre acha a solução.

Usando beforeValidate do CActiveForm, consegui com insatisfação fazer funciona, não queria que funciona-se do jeito que fiz, mais é a única solução que me veio em mente.

Vamos ao passo a passo:

1º Passo: Alterar o Controller.

Exemplo de como deve ficar.




public function actionCreate() {

	$model = new Noticia;


	$this->performAjaxValidation($model);


	if (isset($_POST['Noticia'])) {


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


		if ($model->validate()) {

			if ($model->save()) {


				$json['id'] = $model->primaryKey;

				$json['urlFileUpload'] = url(Yii::app()->controller->module->id . '/' . Yii::app()->controller->id . '/upload', array('id' => $model->primaryKey, 'class' => get_class($model), 'tipo' => 'imagem'));


				Yii::app()->user->setFlash('success', t('Adicionado com sucesso'));

				if (!Yii::app()->request->isAjaxRequest)

					$this->redirect(array('index'));

				else

					$json['redirect'] = url(Yii::app()->controller->module->id . '/' . Yii::app()->controller->id . '/index');

			

			}

		}

	}


	if (!Yii::app()->request->isAjaxRequest)

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

	else

		echo json_encode($json);

}



Obs.: Nas estrutura de condição, eu verifico se estou ou não usando ajax, assim se na hora de validar o formulário e não tiver nenhuma imagem selecionada, ele submete o form sem ajax.

2º Passo.

Alteração do _form.

Eu criei uma função em javascript, que será usado no beforeValidate() no componente do formulário, que por padrão é o CActiveForm(), como eu estou usando o TbActiveForm(), as funções são padrão para os dois.

Está função é nativa no componente, então deve funcionar nas versões em várias versões do framework.

A função básicamente, ela recebe a resposta da validação padrão do formulário, se houver erro o formulário não é submetido, enquanto não ser preenchido os campos obrigatório que foi configurado no seu Model. Agora se não houver erro, ele vai buscar a quantidade de fotos adicionada no plugin jQuery File Upload, se a quantidade for maior que 0(zero) ele submete o formulário em $.ajax(), senão ele submete normal, atualizando a página.

Exemplo da função:




Yii::app()->clientScript->registerScript('_form', "

/**

 * Chamando o plugin fileupload

 */

$('#fileupload').fileupload({

	url: '',

	sequentialUploads: true,

	autoUpload: false,

	formData: function(){ return false; }, //Removo o envio dos dados do formulário, evitando duplicação de dados.

	maxFileSize: 2000000, // 2 MB

	loadImageMaxFileSize: 20000000, // 20MB

	acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,

	maxNumberOfFiles: 5,

	messages: {

		maxNumberOfFiles: '<?php echo t('O número máximo de arquivos excedeu'); ?>',

		acceptFileTypes: '<?php echo t('Tipo de arquivo não permitido'); ?>',

		maxFileSize: '<?php echo t('O arquivo é muito grande'); ?>',

		minFileSize: '<?php echo t('O arquivo é muito pequeno'); ?>'

	}

});


/**

 * Validando imagens com nomes repetidos.

 */

$('#fileupload').bind('fileuploadadd', function(e, data) {

	var currentfiles = [];

	$(this).fileupload('option').filesContainer.children().each(function() {

		if ($(this).data('data') !== undefined) {

			currentfiles.push($(this).data('data').files[0].name);

		} else {

			if ($(this).find('span.preview img').attr('src') !== undefined)

				currentfiles.push($(this).find('span.preview img').attr('src').split('/').pop());

		}

	});

	data.files = $.map(data.files, function(file, i) {

		if ($.inArray(file.name, currentfiles) >= 0) {

			return null;

		}

		return file;

	});

});


var btnClicked; //Variavel que detecta qual botão do formulário foi clicado, para caso tenha mais de 1 botão salvar (Ex.: Salvar e Adicionar novo, Salvar e Continua Editando,...)

jQuery('div.form-actions button').live('click', function(){

    btnClicked = $(this).val();

});

function sendForm(form, data, hasError){

    

    if(hasError){

	// Se houver erro, e feito uma varredura nos campos retornado, e adicionado .focus() no primeiro campo obrigatorio.

        jQuery.each(data, function(index, value){

            jQuery('#' + index).focus();

            return false;

        });

    } else {

	// Está variavel, pega a quantidade de imagens adicionado ao jQuery File Upload.

        var countImagens = $('#fileupload .template-upload:not(\'.error\')').length;

        

	// Se houver imagens, é enviado os dados do formulário em ajax.

        if(countImagens > 0){

            //console.log(btnClicked); //Debug no console para identificar qual botão foi clicado.

			

            jQuery.ajax({

                'type':'POST',

                'url': form.get(0).action,

                'dataType':'json',

                'cache':false,

                'data':form.serialize() + '&botao=' + btnClicked,

                'beforeSend':function(){

		    //Este plugin foi somente um efeite, para bloquear a tela do usuário, caso tenha alguma lentidão no envio dos dados, assim ele não cancela o formulário ou clica novamente em salvar.

		    //http://www.malsup.com/jquery/block/

                    $.blockUI({ message: '<div class=\"progress progress-striped active\"><div class=\"bar\" style=\"width: 100%;\"></div></div>', css: { border: 'none', background: 'transparent'} });

                },

                'success':function(data){

                    //console.log(data); //Debug no console para visualizar se os dados retornado do actionIndex() está correto.

					

		    //Desativo o bloqueio da tela

                    $.unblockUI();


		   // Adiciono a URL de redirecionamento à uma váriavel para ser usando no final do envio das imagens.

                    var redirect = data.redirect;

					

					//Alterado as opções do plugin

                    $('#fileupload').fileupload('option', {

                        url: data.urlFileUpload, //Alterando a URL para que seja enviado as imagens para o action() correto.

                        stop: function(e,data){

                            $('button.cancel').trigger('click'); //Este trigger, ele dispara para o botão "Cancelar Envio" do plugin, para que seja limpado o container, quando existir imagens com erro.

                            location.href=redirect; //Redirecionar o formulário depois de enviar todas as imagens.

                        }

                    });                    

                    

                },

                error: function(jqXHR, textStatus, errorThrown){

                    $.unblockUI();

                    alert(jqXHR.responseText);

                }


            });

            return false;

        }

		

	//Se não houver imagens no container do plugin, e submetido o formulário normalmente

	return true;

        

    }

}

");



Último passo:

Adicionar o ‘clientOptions’ ao componente do formulário.




$form = $this->beginWidget('bootstrap.widgets.TbActiveForm', array(

    'id' => 'index-form',

    'type' => 'horizontal',

    'inlineErrors' => false,

    'enableAjaxValidation' => true,

    'clientOptions' => array('validateOnSubmit' => true, 'validateOnChange' => true,

        'afterValidate' => 'js:sendForm'

    ),

    'htmlOptions' => array(

        'enctype' => 'multipart/form-data'

    ),

        ));



Para quem ta usando o ‘CActiveForm’, adiciona só a linha do ‘clientOptions’.

Para quem for usar este plugin jQuery File Upload, de preferência aos arquivos originais, não usa do YiiBooster, por que ele cria um formulário em volta do container, dando problema caso você adicione dentro de outro formulário.

Já os arquivos do site do plugin, você tem mais controle no layout, fica a dica.

Obs.: Se ainda não usou o plugin, primeiramente baixa ele do site, instala, configura, faz funcionar. Depois faça esta alteração, as dúvidas vai ser relacionada somente a questão de que no cenário create/insert não ter como enviar imagens depois de submeter o formulário, se você estiver enviando imagem somente no cenário update/edit esta explicação não vai lhe servir.

Plugin: http://blueimp.github.io/jQuery-File-Upload/