Los formularios HTML tienen la capacidad de subir archivos de un tamaño arbitrario 39. Los archivos son generalmente transmitidos a través del método POST HTTP 40.
39) La carga de archivos se describen en RFC-1867. Este mecanismo permite cargar grandes archivos usando la codificación de transferencia de contenido binario. El tipo de codificación «multipart/form-data» se utiliza para este propósito.
40) El método HTTP GET es ineficiente para cargar archivos, porque la longitud de la URL tiene un limite superior. Además, la codificación de la URL del archivo de datos incrementa enormemente la longitud de la URL.
Por defecto, HTTP usa la codificación URL para transferir los datos del formulario, podemos ver como esta codificación funciona en los capítulos anteriores. Sin embargo, esta codificación es ineficiente para cargar grandes archivos porque la codificación URL de datos binarios incrementa dramáticamente la longitud de las peticiones HTTP. Si el propósito es cargar archivos se recomienda en su lugar usar la «codificación de transferencia binaria» que se describe en la sección siguiente.
Un formulario HTML simple capaz de cargar archivos se muestra en el código de ejemplo
más abajo. El tipo de codificación binaria se habilita colocando el atributo
enctype
del formulario con el valor «multipart/form-data»:
<form action="upload" method="POST" enctype="multipart/form-data">
<input type="file" name="myfile">
<br/>
<input type="submit" name="Submit">
</form>
En la línea 1, colocamos explícitamente la codificación (atributo enctype
)
«multipart/form-data» para utilizar la codificación de transferencia de contenido
binario en el formulario.
En la línea 2, definimos un campo de entrada de tipo «file» y con el nombre «myfile». Este campo de entrada permitirá al visitante del sitio seleccionar el archivo que se cargará.
Si guardamos el código HTML mencionado arriba en un archivo .html y lo abrimos en nuestro navegador web, veremos una página como la que se muestra en la figura 10.1.
Figura 10.1. Un formulario HTML simple capaz de subir un archivo
El elemento file tiene el botón Browse... que permite seleccionar el archivo que se subirá. Cuando el usuario del sitio selecciona el archivo y hace clic en el botón Submit del formulario, el navegador web enviará una petición HTTP al servidor web y la petición contendrá los datos del archivo que será subido. El ejemplo de abajo ilustra como se ve una petición HTTP:
POST http://localhost/upload HTTP/1.1
Host: localhost
Content-Length: 488
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
Content-Type: multipart/form-data; boundary=----j1bOrwgLvOC3dy7o
Accept-Encoding: gzip,deflate,sdch
------j1bOrwgLvOC3dy7o
Content-Disposition: form-data; name="myfile"; filename="Somefile.txt"
Content-Type: text/html
(file binary data goes here)
------j1bOrwgLvOC3dy7o
Content-Disposition: form-data; name="Submit"
Submit Request
------j1bOrwgLvOC3dy7o--
Como podemos ver en el ejemplo de arriba, la petición HTTP con el tipo de codificación «multipart/form-data» tiene un aspecto análogo a una petición HTTP usual (tiene la línea de estado, las cabeceras y el area de contenido), sin embargo tiene las siguientes e importantes diferencias:
La línea 5 coloca el encabezado «Content-Type» con el valor «multipart/form-data». Los campos que constituyen el formulario están delimitados por una marca límite, boundary. La marca «límite» o «boundary» es una secuencia única y aleatoria de caracteres que delimita los campos del formulario.
Las líneas 8-17 representan el contenido de la petición HTTP. Los campos del formulario se delimitan con una secuencia «límite» (boundary), líneas 8, 13 y 17. Los datos del archivo que son subidos se transmiten en formato binario (línea 12) que permite reducir el tamaño del contenido a su mínimo.
Por defecto, las configuraciones del motor de PHP no permiten cargar grandes archivos (más grandes que 2MB). Para subir grandes archivos, necesitamos editar el archivo de configuración php.ini y modificar los parámetros
post_max_size
yupload_max_filesize
(podemos revisar el Apéndice A. Configuración del Entorno de Desarrollo Web para información de como hacer esto). Colocando estos parámetros en100M
se permite subir archivos de un tamaño de 100 Mb, este valor es generalmente suficiente. Si nuestro plan es subir archivos muy grandes, hasta 1GB, es mejor colocar este valor en 1024M. No olvidemos reiniciar nuestro Servidor Web Apache después de editar el archivo de configuración.
Cuando el visitante del sitio carga algunos archivos al Servidor Web Apache, los
archivo se colocan en una ubicación temporal (generalmente en la carpeta temporal
del sistema, en GNU/Linux /tmp y en Windows C:\Windows\Temp). El código
PHP recibe la información sobre el archivo del arreglo especial super-global $_FILES
.
El arreglo
$_FILES
es análogo a las variables super-globales$_GET
y$_POST
. Estas últimas se usan para guardar las variables GET y POST, respectivamente, mientras la primera se usa para guardar información sobre archivos subidos.
Por ejemplo, para el formulario simple de subida que mencionamos antes, el arreglo
super-global $_FILES
tiene la siguiente forma (la salida se genero con la
función de PHP var_dump()
).
array (size=1)
'myfile' =>
array (size=5)
'name' => string 'somefile.txt' (length=12)
'type' => string 'text/plain' (length=10)
'tmp_name' => string '/tmp/phpDC66.tmp' (length=16)
'error' => int 0
'size' => int 18
Como podemos ver del ejemplo de arriba, el arreglo $_FILES
contiene una entrada
por cada archivo subido. Y por cada archivo subido contiene la siguiente información
As you can see from the example above, the $_FILES
array contains an entry per
each uploaded file. For each uploaded file, it contains the following information:
name
-- nombre original del archivo (línea 4).type
-- tipo MIME 41 del archivo (línea 5).tmp_name
-- nombre temporal del archivo subido (línea 6).error
-- código de error que indica el estado de la subida (línea 7);
el código de error cero significa que el archivo fue subido correctamente.size
-- tamaño del archivo en bytes (línea 8).41) el tipo MIME también conocido como «tipo contenido» (content type) es un identificador estándar en internet para indicar el tipo de dato que contiene el archivo. Por ejemplo el tipo MIME «text/plain» se asigna a un archivo de texto, mientras que el tipo MIME «application/octet-stream» se asigna a un archivo binario.
El motor de PHP almacena los archivos subidos en una ubicación temporal que se limpia
luego de que el script de PHP termina de ejecutarse. Así que, si queremos guardar
el archivos subido en alguna carpeta para usarlo posteriormente, necesitamos usar
la función de PHP move_uploaded_file()
. La función move_uploaded_file()
toma
dos argumentos: el primero de ellos es el nombre del archivo temporal y el segundo
es el nombre del archivo de destino.
Puede confundirnos el hecho de no poder usar la función de PHP
rename()
para mover el archivo subido temporalmente a su ruta de destino. Por razones de seguridad PHP tiene una función especial para mover los archivos subidos. La funciónmove_uploaded_file()
es análoga a la funciónrename()
, pero aquella hace algunas revisiones adicionales para asegurar que el archivo se transfirió a través de una petición HTTP POST y que el proceso de carga terminó sin errores.
El siguiente código de ejemplo muestra como mover el archivo subido con el formulario simple que hemos mostrado arriba:
$destPath = '/path/to/your/upload/dir';
$result = move_uploaded_file($_FILES['myfile']['tmp_name'], $destPath);
if(!$result) {
// Some error occurred.
}
Arriba en la línea 1, creamos la variable $destPath
con el nombre de la carpeta
en donde se guardará el archivo subido.
En la línea 2, llamamos a la función move_uploaded_file()
y le pasamos dos
argumentos: la ruta del archivo temporal y la ruta de destino.
Especificar solo el nombre de la carpeta como segundo argumento de la función
move_uploaded_file()
es útil cuando no queremos renombrar el archivo. Si necesitamos guardar el archivo subido con otro nombre diferente al original debemos especificar la ruta de archivo completa en lugar del nombre de la carpeta.
En la línea 3, revisamos el valor regresado por la función. Si la operación es
exitosa, la función regresará true
. Si ocurre algún error (por ejemplo, si
los permisos de la carpeta no son suficientes para guardar el archivo) se regresará
el booleano false
.