upload bootstrap - ¿Cuál es la mejor manera de crear un formulario de carga de un solo archivo usando PHP?



tags input (5)

He encontrado algunas muestras en línea, pero me gustaría recibir comentarios de personas que usan PHP diariamente sobre posibles consideraciones de seguridad o rendimiento y sus soluciones.

Tenga en cuenta que solo estoy interesado en subir un solo archivo a la vez.

Lo ideal es que no se requiera un complemento de navegador (Flash / Java), aunque sería interesante conocer los beneficios de usar un complemento.

Me gustaría conocer el mejor código HTML y el código de procesamiento PHP.


Answers

El principal beneficio de Flash es que le permite cargar múltiples archivos. El principal beneficio de Java es que permite arrastrar y soltar desde el sistema de archivos. Perdón por no haber respondido la pregunta central, pero pensé en incluir eso porque es bastante simple.


La seguridad es algo muy importante en lo que respecta a la carga de archivos, agregar un .htaccess a la carpeta de carga que detiene la ejecución de las secuencias de comandos desde allí podría ser útil para agregar solo una capa adicional de seguridad.

.htaccess

Options -Indexes
Options -ExecCGI
AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi 

Referencia: http://www.mysql-apache-php.com/fileupload-security.htm


Lea esta introducción que le dirá todo lo que necesita saber. Los comentarios de los usuarios son bastante útiles también.


Tutorial de carga de archivos

HTML

<form enctype="multipart/form-data" action="action.php" method="POST">
  <input type="hidden" name="MAX_FILE_SIZE" value="1000000" />
  <input name="userfile" type="file" />
  <input type="submit" value="Go" />
</form>
  • action.php es el nombre de un archivo PHP que procesará la carga (se muestra a continuación)
  • MAX_FILE_SIZE debe aparecer inmediatamente antes de la entrada con tipo de file . Este valor puede manipularse fácilmente en el cliente, por lo que no se debe confiar en él. Su principal ventaja es proporcionar al usuario una advertencia temprana de que su archivo es demasiado grande, antes de que lo hayan subido.
  • Puede cambiar el nombre de la entrada con el tipo de file , pero asegúrese de que no contenga ningún espacio. También debe actualizar el valor correspondiente en el archivo PHP (a continuación).

PHP

<?php
$uploaddir = "/www/uploads/";
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);

echo '<pre>';
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    echo "Success.\n";
} else {
    echo "Failure.\n";
}

echo 'Here is some more debugging info:';
print_r($_FILES);
print "</pre>";
?>

La carpeta de carga no debe ubicarse en un lugar al que se pueda acceder a través de HTTP; de lo contrario, sería posible cargar un script PHP y ejecutarlo en el servidor.

Imprimir el valor de $_FILES puede dar una pista sobre lo que está sucediendo. Por ejemplo:

    Array
    (
        [userfile] => Array
        (
            [name] => Filename.ext
            [type] => 
            [tmp_name] => 
            [error] => 2
            [size] => 0
        )
    )

Esta estructura proporciona cierta información sobre el nombre del archivo, el tipo MIME, el tamaño y el código de error.

Códigos de error

0 Indica que no hubo errores y el archivo se ha cargado correctamente
1 Indica que el archivo excede el tamaño máximo de archivo definido en php.ini. Si desea cambiar el tamaño máximo de archivo, debe abrir su archivo php.ini, identificar la línea que dice: upload_max_filesize = 2M y cambiar el valor de 2M (2MB) a lo que necesite
2 Indica que se ha excedido el tamaño máximo de archivo definido manualmente, dentro de un script en la página
3 Indica que el archivo solo se ha subido parcialmente
4 Indica que el archivo no ha sido especificado (campo de archivo vacío)
5 Aún no definido
6 Indica que no hay una carpeta temporal
7 Indica que el archivo no se puede escribir en el disco

Configuración php.ini

Al ejecutar esta configuración con archivos más grandes, puede recibir errores. Compruebe su archivo php.ini para estas claves:

max_execution_time = 30
upload_max_filesize = 2M

Aumentar estos valores según corresponda puede ayudar. Cuando se utiliza Apache, los cambios en este archivo requieren un reinicio.

El valor máximo de memoria permitido (establecido a través de memory_limit ) no juega un papel aquí ya que el archivo se escribe en el directorio tmp a medida que se carga. La ubicación del directorio tmp se controla opcionalmente a través de upload_tmp_dir .

Comprobando los tipos mimet

Debería verificar el tipo de archivo de lo que el usuario está cargando: la mejor práctica es validar contra una lista de tipos de archivos permitidos. Un posible riesgo de permitir cualquier archivo es que un usuario podría cargar código PHP al servidor y luego ejecutarlo .

Puede usar la muy útil extensión de información de fileinfo (que reemplaza a la anterior función mime_content_type ) para validar mime-types.

// FILEINFO_MIME set to return MIME types, will return string of info otherwise
$fileinfo = new finfo(FILEINFO_MIME);
$file = $fileinfo->file($_FILE['filename']);

$allowed_types = array('image/jpeg', 'image/png');
if(!in_array($file, $allowed_types))
{
    die('Files of type' . $file . ' are not allowed to be uploaded.');
}
// Continue

Más información

Puede leer más sobre el manejo de cargas de archivos en el manual de PHP.net .

Para PHP 5.3+

//For those who are using PHP 5.3, the code varies.
$fileinfo = new finfo(FILEINFO_MIME_TYPE);
$file = $fileinfo->file($_FILE['filename']['tmp_name']);
$allowed_types = array('image/jpeg', 'image/png');
if(!in_array($file, $allowed_types))
{
    die('Files of type' . $file . ' are not allowed to be uploaded.');
}
// Continue

Más información

Puede leer más en FILEINFO_MIME_TYPE en la documentación de PHP.net .


La forma más sencilla y sólida en que lo he hecho en el pasado, es simplemente apuntar una etiqueta iFrame oculta con su formulario, luego se enviará dentro del iframe sin volver a cargar la página.

Es decir, si no desea utilizar un complemento, JavaScript o cualquier otra forma de "magia" que no sea HTML. Por supuesto que puedes combinar esto con JavaScript o lo que tengas ...

<form target="iframe" action="" method="post" enctype="multipart/form-data">
    <input name="file" type="file" />
    <input type="button" value="Upload" />
</form>

<iframe name="iframe" id="iframe" style="display:none" ></iframe>

También puede leer el contenido del iframe onLoad para los errores del servidor o las respuestas exitosas y luego onLoad al usuario.

Chrome, iFrames y onLoad

-nota- solo debe seguir leyendo si está interesado en cómo configurar un bloqueador de UI al realizar la carga / descarga

Actualmente, Chrome no activa el evento onLoad para el iframe cuando se utiliza para transferir archivos. Firefox, IE y Edge activan el evento onload para transferencias de archivos.

La única solución que encontré que funciona para Chrome era usar una cookie.

Para hacer eso básicamente cuando se inicia la carga / descarga:

  • [Cliente] Comenzar un intervalo para buscar la existencia de una cookie
  • [Server Side] Haga lo que necesite con los datos del archivo
  • [Server Side] Configurar cookie para el intervalo del lado del cliente
  • [Client Side] Interval ve la cookie y la usa como el evento onLoad. Por ejemplo, puede iniciar un bloqueador de la interfaz de usuario y luego onLoad (o cuando se crea una cookie) se elimina el bloqueador de la interfaz de usuario.

Usar una cookie para esto es feo pero funciona.

Hice un complemento de jQuery para manejar este problema para Chrome al descargar, lo puedes encontrar aquí

https://github.com/ArtisticPhoenix/jQuery-Plugins/blob/master/iDownloader.js

El mismo principio básico se aplica a la carga, también.

Para usar el descargador (incluir el JS, obviamente)

 $('body').iDownloader({
     "onComplete" : function(){
          $('#uiBlocker').css('display', 'none'); //hide ui blocker on complete
     }
 });

 $('somebuttion').click( function(){
      $('#uiBlocker').css('display', 'block'); //block the UI
      $('body').iDownloader('download', 'htttp://example.com/location/of/download');
 });

Y en el lado del servidor, justo antes de transferir los datos del archivo, cree la cookie

 setcookie('iDownloader', true, time() + 30, "/");

El complemento verá la cookie y luego activará la onComplete llamada onComplete .





php upload file-upload