subir ficheros con swfupload componente cakephp
Existe una herramienta genial para subir ficheros a un servidor con PHP, Javascript y un flash, se llama SwfUpload y muchos ya la conocerán, pero integrarla en CakePHP ha sido un poco caos porque los artículos que hay están desactualizados, hechos para versiones obsoletas de SwfUpload o para la versión 1.1 de CakePHP, por lo que he tenido que tocar mucho.
En el siguiente artículo de CakePHP lo explican mas o menos http://bakery.cakephp.org/articles/view/swfupload-and-multipurpose-uploader y en la documentación oficial de SwfUpload te envían al artículo, asi que tampoco es que sea una maravilla.
Yo voy a explicar lo que he hecho para integrar el SwfUpload en CakePHP con las versiones mas actuales ahora mismo: CakePHP: 1.2 y SwfUpload 2.2.0.1
Lo primero el Componente. Que es lo único que he copiado íntegro del artículo. y guardarlo en app/controllers/components/swf_upload.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
<?php
/* $Id$ */
/**
* SwfUploadComponent - A CakePHP Component to use with SWFUpload
* Copyright (C) 2006-2007 James Revillini <james at revillini dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* SwfUploadComponent - A CakePHP Component to use with SWFUpload
* Thanks to Eelco Wiersma for the original explanation on handling
* uploads from SWFUpload in CakePHP. Also, thanks to gwoo for guidance
* and help refactoring for performance optimization and readability.
* @author James Revillini http://james.revillini.com
* @version 0.1.4
*/
/**
* @package SwfUploadComponent
* @subpackage controllers.components
*/
class SwfUploadComponent extends Object {
/* component configuration */
var $name = 'SwfUploadComponent';
var $params = array();
var $errorCode = null;
var $errorMessage = null;
// file and path configuration
var $uploadpath;
var $webpath = '/files/';
var $overwrite = false;
var $filename;
/**
* Contructor function
* @param Object &$controller pointer to calling controller
*/
function startup(&$controller) {
// initialize members
$this->uploadpath = 'files' . DS;
//keep tabs on mr. controller's params
$this->params = $controller->params;
}
/**
* Uploads a file to location
* @return boolean true if upload was successful, false otherwise.
*/
function upload() {
$ok = false;
if ($this->validate()) {
$this->filename = $this->params['form']['Filedata']['name'];
$ok = $this->write();
}
if (!$ok) {
header("HTTP/1.0 500 Internal Server Error"); //this should tell SWFUpload what's up
$this->setError(); //this should tell standard form what's up
}
return ($ok);
}
/**
* finds a unique name for the file for the current directory
* @param array an array of filenames which exist in the desired upload directory
* @return string a unique filename for the file
*/
function findUniqueFilename($existing_files = null) {
// append a digit to the end of the name
$filenumber = 0;
$filesuffix = '';
$fileparts = explode('.', $this->filename);
$fileext = '.' . array_pop($fileparts);
$filebase = implode('.', $fileparts);
if (is_array($existing_files)) {
do {
$newfile = $filebase . $filesuffix . $fileext;
$filenumber++;
$filesuffix = '(' . $filenumber . ')';
} while (in_array($newfile, $existing_files));
}
return $newfile;
}
/**
* moves the file to the desired location from the temp directory
* @return boolean true if the file was successfully moved from the temporary directory to the desired destination on the filesystem
*/
function write() {
// Include libraries
if (!class_exists('Folder')) {
uses ('folder');
}
$moved = false;
$folder = new Folder($this->uploadpath, true, 0755);
if (!$folder) {
$this->setError(1500, 'File system save failed.', 'Could not create requested directory: ' . $this->uploadpath);
} else {
if (!$this->overwrite) {
$contents = $folder->ls(); //get directory contents
$this->filename = $this->findUniqueFilename($contents[1]); //pass the file list as an array
}
if (!($moved = move_uploaded_file($this->params['form']['Filedata']['tmp_name'], $this->uploadpath . $this->filename))) {
$this->setError(1000, 'File system save failed.');
}
}
return $moved;
}
/**
* validates the post data and checks receipt of the upload
* @return boolean true if post data is valid and file has been properly uploaded, false if not
*/
function validate() {
$post_ok = isset($this->params['form']['Filedata']);
$upload_error = $this->params['form']['Filedata']['error'];
$got_data = (is_uploaded_file($this->params['form']['Filedata']['tmp_name']));
if (!$post_ok){
$this->setError(2000, 'Validation failed.', 'Expected file upload field to be named "Filedata."');
}
if ($upload_error){
$this->setError(2500, 'Validation failed.', $this->getUploadErrorMessage($upload_error));
}
return !$upload_error && $post_ok && $got_data;
}
/**
* parses file upload error code into human-readable phrase.
* @param int $err PHP file upload error constant.
* @return string human-readable phrase to explain issue.
*/
function getUploadErrorMessage($err) {
$msg = null;
switch ($err) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_INI_SIZE:
$msg = ('The uploaded file exceeds the upload_max_filesize directive ('.ini_get('upload_max_filesize').') in php.ini.');
break;
case UPLOAD_ERR_FORM_SIZE:
$msg = ('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.');
break;
case UPLOAD_ERR_PARTIAL:
$msg = ('The uploaded file was only partially uploaded.');
break;
case UPLOAD_ERR_NO_FILE:
$msg = ('No file was uploaded.');
break;
case UPLOAD_ERR_NO_TMP_DIR:
$msg = ('The remote server has no temporary folder for file uploads.');
break;
case UPLOAD_ERR_CANT_WRITE:
$msg = ('Failed to write file to disk.');
break;
default:
$msg = ('Unknown File Error. Check php.ini settings.');
}
return $msg;
}
/**
* sets an error code which can be referenced if failure is detected by controller.
* note: the amount of info stored in message depends on debug level.
* @param int $code a unique error number for the message and debug info
* @param string $message a simple message that you would show the user
* @param string $debug any specific debug info to include when debug mode > 1
* @return bool true unless an error occurs
*/
function setError($code = 1, $message = 'An unknown error occured.', $debug = '') {
$this->errorCode = $code;
$this->errorMessage = $message;
if (DEBUG) {
$this->errorMessage .= $debug;
}
return true;
}
}
?>
He creado un controller para procesar todos las imágenes subidas. app/controllers/archivos_controller.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?php
class ArchivosController extends AppController
{
var $components = array('SwfUpload');
function __construct()
{
parent::__construct();
$this->layout = 'ajax';
$this->autoRender = false;
Configure::write('debug',0);
}
function subir()
{
# Controlo donde subirlo y cambio los path
if (isset($this->params['pass']['0']))
{
switch($this->params['pass']['0'])
{
case 'video':
$this->SwfUpload->uploadpath = APP . WEBROOT_DIR . DS . 'files' . DS . 'videos';
$this->SwfUpload->webpath = '/upload_videos/' ;
break;
case 'cms':
$this->SwfUpload->uploadpath = APP . WEBROOT_DIR . DS . 'files' . DS . 'cms';
$this->SwfUpload->webpath = '//' ;
break;
# etc.. lo que querais
}
}
# funciones para subir los ficheros.
if (isset($this->params['form']['Filedata']))
{
if ($this->SwfUpload->upload())
{
$this->params['form']['Filedata']['name'] = $this->SwfUpload->filename;
$this->params['form']['Filedata']['path'] = $this->SwfUpload->webpath;
$this->params['form']['Filedata']['fspath'] = $this->SwfUpload->uploadpath . $this->SwfUpload->filename;
$this->data['Archivo'] = $this->params['form']['Filedata'];
# guardamos el resultado en BBDD
if (!($file = $this->Archivo->save($this->data)))
{
$this->Session->setFlash('Guardado con éxito');
}
else
{
$this->Session->setFlash('Archivo subido: ' . $this->SwfUpload->filename . '; Id en base de datos: ' . $this->Archivo->getLastInsertId() . '.');
echo $this->Archivo->getLastInsertId();
exit;
}
}
else
{
$this->Session->setFlash($this->SwfUpload->errorMessage);
}
}
}
/**
* carga un archivo conociendo su id
*
* @param mixed $id
*/
function abrir($id)
{
$file = $this->get($id);
if (isset($file)) {
$this->redirect($file['Archivo']['path'] . $file['Archivo']['name']);
exit();
}
}
function get($id)
{
# sacamos la informacion del fichero
$file = $this->Archivo->findById($id);
return $file;
}
}
?>
Creamos el Modelo y a continuación incluyo la SQL para crear la tabla
1
2
3
4
5
6
7
8
<?php
class Archivo extends AppModel
{
var $name = 'archivo';
}
?>
SQL para crear la tabla
1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE `archivos` (
`id` int(11) NOT NULL auto_increment,
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`updated` datetime NOT NULL default '0000-00-00 00:00:00',
`path` varchar(255) NOT NULL default '',
`name` varchar(255) NOT NULL default '',
`fspath` varchar(255) default NULL,
`type` varchar(255) NOT NULL default '',
`size` int(11) NOT NULL default '0',
`deleted` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`id`),
UNIQUE KEY `path_name_idx` (`path`,`name`)
) ENGINE=InnoDB CHARSET=utf8
Bueno pues hasta aquí la cosa ya está casi al 60% para terminar de integrar SwfUpload con CakePHP ahora sólo tenemos que descargarnos las librerías javascript, flash y css. Esto lo podeis hacer desde aquí: http://code.google.com/p/swfupload/downloads/list
Antes de hacer todo esto os aconsejaría que probarais a hacerlo con PHP simple y vierais como funciona, así tendríais una mejor visión. Yo me descargué la version SWFUpload v2.2.0.1 Core.zip y SWFUpload v2.2.0.1 Samples.zip, son prácticamente iguales.
Mi arbol de archivos quedó de la siguiente manera
- webroot/js/swfupload/
- webroot/js/swfupload/images/
- webroot/js/swfupload/images/XPButtonUploadText_61x22.png
- webroot/js/swfupload/fileprogress.js
- webroot/js/swfupload/handlers.js
- webroot/js/swfupload/swfupload.js
- webroot/js/swfupload/swfupload.queue.js
- webroot/js/swfupload/swfupload.swf
-
webroot/js/swfupload/swfuploadbutton.swf
- webroot/css/swfupload/default.css
Éstas librerías las cargo de la siguiente manera en la vista.
app/views/posts/subir_imagenes.ctp (o cualquier vista desde la que queráis subir imágenes)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?
# incluyo el css de swfupload
echo $html->css('swfupload/default'). " n";
# incluyo las librerias de javascript para el swfupload
echo $javascript->link('swfupload/swfupload', NULL, false);
echo $javascript->link('swfupload/swfupload.queue', NULL, false);
echo $javascript->link('swfupload/fileprogress', NULL, false);
echo $javascript->link('swfupload/handlers', NULL, false);
# a continuación añado la llamada javascript que inicia y carga el formulario de SWFupload
?>
<script type="text/javascript">
var swfu;
window.onload = function() {
var settings = {
flash_url : "/js/swfupload/swfupload.swf",
upload_url: "/archivos/subir/cms",
post_params: {"PHPSESSID" : "<?php echo session_id(); ?>"},
file_size_limit : "100 MB",
file_types : "*.*",
file_types_description : "All Files",
file_upload_limit : 100,
file_queue_limit : 0,
custom_settings : {
progressTarget : "fsUploadProgress",
cancelButtonId : "btnCancel"
},
debug: false,
// Configuración del botón de "upload"
button_image_url: "/js/swfupload/images/XPButtonUploadText_61x22.png",
button_width: "61",
button_height: "22",
button_placeholder_id: "spanButtonPlaceHolder",
button_text: '',
button_text_style: ".theFont { font-size: 16; }",
button_text_left_padding: 12,
button_text_top_padding: 3,
file_queued_handler : fileQueued,
file_queue_error_handler : fileQueueError,
file_dialog_complete_handler : fileDialogComplete,
upload_start_handler : uploadStart,
upload_progress_handler : uploadProgress,
upload_error_handler : uploadError,
upload_success_handler : uploadSuccess,
upload_complete_handler : uploadComplete,
queue_complete_handler : queueComplete
};
swfu = new SWFUpload(settings);
};
</script>
<form>
<!-- div contenedores del formulario de SwfUpload y donde se mostrará el proceso de carga -->
<div class="fieldset flash" id="fsUploadProgress">
<span class="legend">Subir Ficheros</span>
</div>
<div id="divStatus">Pulsa en upload para seleccionar las imágenes.<br />Puede subir un máximo de tres imagenes por producto, de 2MB de máximo cada uno. Solo admite JPG y GIF y es aconsejable que en su titulos no haya caracteres extraños tales como `, *, o ñ. Debe esperar unos segundos hasta que se completa la transferencia.</div>
<div>
<span id="spanButtonPlaceHolder"></span>
<!-- stilos del boton cancelear margin-left: 2px; font-size: 8pt; height: 29px;-->
<input id="btnCancel" type="button" value="Cancelar" onclick="swfu.cancelQueue();" disabled="disabled" style="" />
</div>
</form>
En el script anterior que inicia el formulario de swfupload hay variables de configuracion que a mi me han servido, se pueden cambiar y poner las que queráis, echar un ojo a la versión SWFUpload v2.2.0.1 Samples.zip para ver los diferentes tipos de botones que hay, etc..
El formulario lo tendreis que crear como normalmente lo hagáis ya sea con el helper Form de CakePHP o con HTML normal, yo lo he puesto algo de ejemplo.
Cualquier duda escribir un comentario…
Espero que os ayude!
ACTUALIZACIÓN:
Adjunto un zip con toda la estructura del ejemplo: [download id="1" format="4" autop="false"]