Introducción

Nota: ya hay una versión 3 de este código. En ese nuevo post explico cómo dar la posibilidad de que el usuario cambie la cámara, además de que introduzco otras mejoras y actualizaciones. Míralo aquí.

Nunca imaginé que algún día se podría tomar una foto y guardarla en un servidor usando código nativo de Javascript y la cámara del dispositivo. Eso abre un mundo de posibilidades que permite a nuestras aplicaciones tener más características.

Hoy mostraré aquí un pequeño tutorial que nos permitirá tomar una simple foto y subirla a un servidor que tendrá PHP. No se usará ningún framework, ni de Javascript ni de PHP.

Nota: debido a que vamos a tomar una foto con la cámara, debemos servir nuestra app en localhost (para hacer pruebas locales) o en un servidor con https. Es decir, nuestro código debe estar en un servidor con un certificado SSL, o corriendo en nuestra máquina.

Si te gusta programar en Python, te invito a leer cómo tomar una foto de la cámara web, utilizando Python.

Probar proyecto terminado

Online

Si quieres ver el resultado en vivo, sin descargar nada, pues hacer click aquí.

Nota: no me hago responsable de las fotos que tomes, no se borrarán y se quedarán ahí hasta que tenga la oportunidad de eliminarlas periódicamente.

Descargar

También puedes descargar este archivo, extraerlo y pegarlo en la raíz de tu servidor local. Si usas XAMPP (en este tutorial explico cómo configurarlo), este podría ser C:\xampp\htdocs.

Programando la parte del cliente

Consiguiendo permiso

Para acceder a la cámara del dispositivo es necesario tener permiso del usuario (si no, imagina: cualquier usuario tendría acceso para tomarte fotografías y vídeos con sólo visitar una página web).

Antes de pedir acceso, debemos comprobar si el navegador del usuario tiene soporte. Como no sabemos cuál es el que usa, y como tampoco podemos imaginar un escenario perfecto en donde todos usen Chrome, debemos considerar todas las posibilidades.

El siguiente código comprueba si existe al menos una de las funciones. Primero prueba con la que debería ser de Chrome, después con Mozilla y así. Si no encuentra ninguna, entonces regresa false.

Una vez que tenemos la función para verificar, debemos crear otra función que envuelva a todas las posibles opciones. Es decir, que devuelva la función para pedir permiso independientemente del navegador. Para ello, podemos crear una de la siguiente forma:

Con todo esto, finalmente podremos pedir permiso sólo si el navegador tiene soporte. Es buen momento para describir los argumentos que toma la función getUserMedia.

  1. Restricciones: aquí pedimos lo que queremos obtener. Debemos indicar si queremos vídeo y audio con una variable booleana. En este caso sólo pondré video a true, porque el audio no es necesario.
  2. Función en caso de aprobación: si el usuario da permiso, se llamará a esta función que traerá el stream como parámetro.
  3. Función en caso de rechazo: si el usuario deniega el servicio o existe algún error, se llamará esta función que traerá el error como parámetro.

Una vez explicado esto, ahora sí podemos pedir permiso de la siguiente manera:

Nota: por si no lo sabes, para abrir la consola y ver los mensajes puedes presionar F12 en Chrome, Mozilla y Edge. Y Ctrl + Shift + I en Opera.

Mostrando stream

Ya tenemos el stream, pero no lo estamos mostrando. Se supone que debemos mostrar al usuario lo que su cámara ve, y cuando él quiera, tomar la foto. Así que vamos a definir un elemento video:

<video id="video"></video>

En el código haremos referencia a él y mostraremos el stream:

Con esto, mostraremos al usuario lo que su cámara ve.

Agregando botón y canvas para tomar foto

Abajo del vídeo agregaremos un botón. También hará falta un elemento canvas para pintar ahí la foto tomada. Ahora que lo pienso, necesitamos un elemento para mostrarle al usuario el estado; es decir, si la foto ya se guardó, si se está enviando, etcétera. Para ello pondré un párrafo vacío.

El HTML quedaría así:

Notar por favor que el canvas está oculto, porque no sería necesario mostrarlo. Simplemente lo usaremos para poner ahí la imagen.

El código que define a los elementos está hasta ahora así:

Ahora es momento de escuchar el click del botón y al hacerlo tomar la foto. El código quedaría así:

De esta forma hacemos una petición POST usando AJAX al servidor. Enviamos la foto que estaba en el canvas en base64. Se supone que el servidor nos debería responder con el nombre de la imagen guardada.

Recibirla y guardarla en el servidor es otra historia que veremos abajo. Hasta aquí termina la programación del lado del cliente.

Importante: Actualización abril 2018

Chrome genera una advertencia indicando que URL.createObjectURL es obsoleto, así que tenemos que actualizarnos.

Básicamente es cambiar esta línea:

$video.src = window.URL.createObjectURL(stream);

Por esto:

$video.srcObject = stream;

Más información aquí: Tomar foto de cámara web con Javascript, actualizado

Programando servidor

Ahora es el turno de PHP. Vamos a recibir la imagen, decodificarla y guardarla. Algo muy importante para tomar en cuenta es que al obtenerla desde el canvas, se agrega “data:image/png;base64,” al inicio de la cadena. Si la guardamos así, no será una imagen válida; tenemos que remover esa parte.

Recordemos también que la imagen se codifica para que viaje sin alterarse, y sin tener su forma original.

Dicho esto, aquí está el código PHP:

Los datos enviados por ajax los obtenemos leyendo el stream de php://input. Comprobamos si se mandó algo y si es que sí, lo recuperamos.

Eso lo decodificamos y limpiamos para finalmente hacer uso de file_put_contents para escribir la imagen en un fichero. uniqid es usado para generar, como su nombre lo dice, una cadena única. Así, aunque se envíen muchas imágenes todas tendrán un nombre distinto.

Al terminar, salimos y mostramos los bytes que se escribieron. No es necesario capturar esa respuesta

¿Y en dónde veo las fotos que he tomado? muy fácil, si vamos al directorio en donde reside el archivo php, las imágenes estarán ahí mismo. Por ejemplo, mi carpeta luce así:

Si lo descargas y pruebas tú mismo, o pruebas la versión online, al tomar la foto saldrá un enlace para que la veas.

Referencias

Algunas de las cosas que aparecen en este post no las sabía, y las aprendí de algunos sitios. Aquí dejo los enlaces:

MDN – Navigator.getUserMedia

Ajax sin jQuery


Estoy disponible para trabajar en tu proyecto o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.
Si el post fue de tu agrado muestra tu apoyo compartiéndolo, suscribiéndote al blog, siguiéndome o realizando una donación.

Suscribir por correo

Ingresa tu correo y recibirás mis últimas entradas sobre programación, open source, bases de datos y todo lo relacionado con informática

Únete a otros 3,136 suscriptores


parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

51 Comentarios

Max · noviembre 22, 2021 a las 3:20 pm

Te agradezco mucho, muy interesante tu script para tomar fotos, si fuera posible, quisiera saber que puedo hacer para que funcione en un navegador Safari, debido a que, en IPHONE e IPAD no se activa la función de vídeo, es decir, sólo funciona en Chrome y en EDGE ¿debo comentar alguna parte del código Javascript o que puedo hacer? Gracias

    parzibyte · noviembre 22, 2021 a las 3:30 pm

    Solo debe dejar de usar Safari y usar un navegador compatible, como Google Chrome. De otro modo no es posible
    Saludos!

andres · julio 16, 2021 a las 7:40 pm

probé tu código y funciona de maravilla, pero al querer usarlo en un ipad no me muestra ninguna cámara, ni me pide permisos de activar cámara, que puede ser? sera que hay que modificar algo? gracias

jose · agosto 27, 2020 a las 4:02 pm

como podría guardar el nombre de la imagen y guardar la imagen en una carpeta. espero me puedas ayudar.

    parzibyte · agosto 27, 2020 a las 4:15 pm

    Hola, cuando se invoque a file_put_contents puede cambiar tanto el nombre como la ruta.
    Saludos 🙂

Julio Toledo · agosto 15, 2020 a las 7:04 pm

Hola, ¿cómo puedo guradar la imagen en formato jpg?

    parzibyte · agosto 15, 2020 a las 8:28 pm

    Hola. Aunque suene un poco obvio, usted debe convertir la imagen PNG a JPG con cualquier librería que le parezca, por ejemplo, ImageMagick
    Saludos 🙂

Julio Toledo · agosto 5, 2020 a las 1:34 pm

Gracias por compartir tu conocimiento. Quisiera saber si hay manera de mejorar la calidad de las fotos ya que al hacer zoom se pixelea demasiado.
Saludos

    parzibyte · agosto 5, 2020 a las 2:21 pm

    Me parece que se puede especificar la resolución de la foto o cámara con algunas constraints al invocar a getUserMedia, sería cuestión de investigar y probar.
    Saludos

    Julio Toledo · agosto 5, 2020 a las 9:43 pm

    Hola de nuevo, cuando tomo fotos con celular guarda dos veces la foto. ¿A qué se debe? probé con celular de un solo lente.

    parzibyte · agosto 5, 2020 a las 11:24 pm

    Hola. Puede ser que el listener del botón se esté agregando dos veces

Gómez · agosto 4, 2020 a las 7:51 pm

Hola me interesa saber como se guarda la imagen con un nombre que sea ingresado desde una caja de texto. Agradezco tu ayuda.

gean · abril 16, 2020 a las 12:13 am

hola , muy buen código , lo probé en local y bien , pero cuando lo subí al servidor , este no funciona , no encuentra los dispositivos de cámara para ser utilizados.

    parzibyte · abril 16, 2020 a las 2:11 pm

    Ha de ser que tu servidor no tiene HTTPS

Antonio · abril 1, 2020 a las 2:11 pm

Muy buen código, gracias por compartirlo!
Sólo una pregunta: ¿Sería posible tomar varias fotos sin subirlas y mediante un botón de ‘Aceptar’ se suban todas a la vez?

    parzibyte · abril 2, 2020 a las 11:00 am

    Me parece que es posible, podrías crear un FormData y agregarle cada foto, para enviarlo después cuando se haga click en el botón.
    Saludos.

Yesid Araque · febrero 4, 2020 a las 12:05 pm

Un excelente programa pero tengo un problema debo de generar un submit desde el .js para que me cargue el realize varios procedimientos pero no me funciona me podrias el nombre del formulario y .submit(); y no me funciona gracias por tu ayuda

    parzibyte · febrero 4, 2020 a las 2:26 pm

    Gracias por tus comentarios. Me parece que ya te respondí en Facebook.
    Saludos 🙂

Daniel · noviembre 5, 2019 a las 9:09 pm

Parzibyte
Buenas noches
Muy bueno tu codigo, te felicito.
Tengo una duda que no se como resolverla o que parte del codigo tocar.
Quiero guardar la imagen en otra carpeta, por ejemplo foto_capturada, dentro de la carpeta que viene por defecto que se llama fotos_js-master.

Que linea de codigo debo modificar y que poner en ella
Desde ya muchas gracias

    parzibyte · noviembre 6, 2019 a las 11:14 am

    Hola Daniel buen día. Debes crear la carpeta llamada foto_capturada; si quieres puedes hacerlo manualmente o en PHP usando:
    mkdir("foto_capturada");

    Después en la línea en donde se guarda, indicar la ruta, así:
    $nombreImagenGuardada = "./foto_capturada/foto_" . uniqid() . ".png";

    Y con eso debería bastar.

    Daniel · noviembre 6, 2019 a las 11:50 am

    parzibyte
    Buenas tardes
    La indicacion que me diste funciona muy bien.
    Desde ya muchas gracias.
    Ahora estoy intentandolo integrar todo en un MODAL de Boostrap 4
    Cualquier duda te molesto.
    Nuevamente milgracias y vuelvo a felicitarte por el Scropt

Jorge · agosto 7, 2019 a las 10:27 am

Hola ya lo probé pero se queda en:

Enviando foto. Por favor, espera…

y no la guarda en el folder lo levante en Wamp server

    parzibyte · agosto 7, 2019 a las 10:53 am

    Te recomiendo abrir la consola del navegador para verificar posibles errores, y comprobar que el servidor está en el puerto correcto (80), además de la existencia del archivo PHP.
    Si sigues experimentando problemas y necesitas ayuda personalizada no dudes en contactarme: https://parzibyte.me/blog/contrataciones-ayuda/

Heribertohernan · junio 18, 2019 a las 11:08 am

Gracias mi hermano excelente código me sirvió bastante bien, saludos de Puebla México

Enrique · enero 17, 2019 a las 11:41 am

Me parece fantástico como se muestra y guarda el archivo en el equipo, pero como se puede tomar alguna variable como por ejemplo que se escriba un nombre y que el archivo de la foto ademas de la foto se muestre con el nombre.png

    parzibyte · enero 17, 2019 a las 11:47 am

    Hola, para pedir el nombre puedes poner un campo de texto, leerlo y enviarlo junto con la foto. No hay límite en los datos que puedes enviar. Y para renombrar la imagen puedes no usar uniqid.

    Espero que eso resuelva tu duda, te invito a seguirme en mis redes sociales y también a suscribirte a mi blog 🙂

    Enrique · enero 17, 2019 a las 2:30 pm

    Gracias ya pude cambiar la ubicación de directorio donde se guardan la fotos así como la de la resolución en la que guarda, también genere el input desde la primera pagina para escribir el nombre sel archivo pero no logro hacer que aparezca al en el nombres sel archivo solo logra agregarle fecha, hora y un carácter cambiando el uniqid que me comentaste, solo me hace falta lo del nombre escrito.

    parzibyte · enero 18, 2019 a las 9:48 am

    Crea un input, supongamos que tiene el id “inputNombre”. Al momento de enviar los datos haz lo que dice lo siguiente:
    https://gist.github.com/parzibyte/936dd10deef3332b427d3cdd76a65490
    Eso hará que se envíe un JSON con ambos datos, ahora del lado del servidor haz lo que dice lo siguiente:
    https://gist.github.com/parzibyte/4d4f2bc81b753121c6c6f6afbb0464fc
    Y listo, ya puedes hacer lo que quieras con el nombre y la imagen, espero que haya servido, si no, en resumen:
    Envía un objeto JSON con los datos necesarios, y decodifica del otro lado. Más información sobre JSON y PHP: https://parzibyte.me/blog/2018/12/26/codificar-decodificar-json-php/
    Y sobre JavaScript y JSON: https://parzibyte.me/blog/2018/12/24/codificar-decodificar-json-javascript/

pedro lopez · noviembre 6, 2018 a las 7:16 pm

hola, como estas? implemente tu codigo y me pasa algo raro y es que al tomar la foto y guardarla en la base de datos refresca por completo la pagina y a veces si toma la foto y se queda normal como deberia ser, y lo otro es que cuando se queda normal la camara no se apaga al menos que refresque la pagina.

Omar Larriva León · septiembre 27, 2018 a las 7:34 pm

Me encantó tu publicación y me es de gran ayuda.
Use tus archivos y de forma local me resulta excelente, pero cuando lo tomo desde otro equipo no me detecta la cámara.
Que es lo que se tiene que modificar para que funcione??
Respecto al método de almacenar el nombre de la foto, la forma mas fácil de modificar el nombre del archivo es utilizando variables de sesión, al insertar el campo y asignarle el valor de una variable de sesión, posteriormente puedes utilizarla para asignar ese valor en el nombre de tu foto.
Así siempre generarías una foto diferente para cada valor de tu base sin necesidad de guardar el dato en un servidor.

    parzibyte · septiembre 27, 2018 a las 9:38 pm

    Hola, tal vez:
    No hay controladores de la cámara
    No estás cargando el sitio con https
    Los permisos fueron denegados anteriormente
    El navegador está desactualizado

    Y gracias por tus comentarios 🙂 saludos

    Omar Larriva León · octubre 8, 2018 a las 2:53 pm

    ya tengo todo configurado, pero solamente me funciona mi certificado que cree con OPENSSL de forma local y no me da salida, revisé mis puertos en el Firewall y estan abiertos (80 y 443). Que podrá ser??

    parzibyte · octubre 9, 2018 a las 11:43 am

    Hola. ¿Qué error marca? ¿Lo has probado con otros navegadores o desde otra computadora? prueba, y si sigue sin funcionar por favor dime cuál error arroja. Saludos 🙂

Franco · agosto 26, 2018 a las 6:31 pm

Hola, está muy bueno tu tutorial, pero tengo un problema, que la foto que tomo no la guarda en la carpeta contenedora del php, la carpeta la descomprimí en Descargas

Jacques Salinas · agosto 14, 2018 a las 10:39 am

Hola Buenos días, estoy intentando usar tu código en el celular y me dice que no se puede acceder a la cámara , o no diste permiso, pero a mi no me preguntaron.

    parzibyte · agosto 14, 2018 a las 10:50 am

    Hola. Trata de abrir un navegador actualizado como Chrome o Firefox. Tal vez sea que tu navegador no soporta el acceso a la cámara. Saludos.

    Jacques Salinas · agosto 14, 2018 a las 11:38 am

    Lo abrí con el Chrome del celular que está actualizado, me interesa mucho porque estoy haciendo una especie de módulo de accesso.

    parzibyte · agosto 14, 2018 a las 12:07 pm

    Seguramente denegaste el permiso la primera vez. De todos modos, puedes reiniciar la configuración.
    Una vez dentro de la página, accede a Menú Configuración Configuración del sitioCámara Selecciona https://cgfAcceso a la cámaraPermitir

    En mi teléfono se ve así: https://i.imgur.com/AsoXFOC.jpg

    Espero que se solucione. Y si no, prueba con otro navegador. Tal vez el problema sea Chrome

Naty Rosero · agosto 9, 2018 a las 3:33 pm

Hola, gracias por esta explicación muy interesante, quiero implementar tu código pero me sale un error
Uncaught TypeError: Cannot set property ‘srcObject’ of null
at script.js:26
me podrías dar una guía de como solventar esto, de antemano muchs gracias

    parzibyte · agosto 9, 2018 a las 4:04 pm

    Hola. Es porque $video es null. ¿El elemento

Gregory Duarte · junio 22, 2018 a las 4:30 pm

Saludos, si yo quisiera almacenar esa imagen direcatmente en una de las columnas de una base de datos especifica, cual seria la manera?

    parzibyte · junio 22, 2018 a las 4:55 pm

    Hola, se me ocurren 2 opciones. Una:
    Como puedes ver, en PHP obtenemos el nombre de la imagen, que no es más que un id. Puedes almacenar ese nombre en tu base de datos, y finalmente cuando recuperes las imágenes, ponerlo en el atributo src. Por ejemplo: <img src=”directorio_en_donde_guardas_las_imagenes/$nombre_recuperada_de_base_de_datos”>

    Ese método es el que recomiendo. Pero hay otro:
    Puedes guardar toda la cadena que está en base64, y al mostrar las imágenes poner lo siguiente: <img src=”$base_64_recuperado_de_la_base_de_datos”>

    Por cierto, para que el segundo método funcione no debes hacer el str_replace, ya que debes dejar la cadena intacta.

    La diferencia entre las dos opciones es que una sólo guarda el nombre en la base de datos, y la imagen en el disco duro del servidor. En cambio, el segundo guarda toda la imagen en la base de datos (aunque igualmente la base de datos está sobre el disco duro, pero recuerda que una base de datos entre menos pesada trabaja mejor y más rápido)

    Finalmente, si no tienes una idea clara de cómo trabajar con una base de datos y PHP, te dejo este tutorial: https://parzibyte.me/blog/2018/02/12/mysql-php-pdo-crud/
    Está escrito para MySQL pero supongo que ese es el que estás usando, y si no, dímelo para que escriba sobre ello.
    Saludos 🙂

carla · febrero 27, 2018 a las 9:23 am

Genial! te felicito por la página. Me ayudaste un montón, más de lo que he visto en otros lados.
Gracias totales.!

Grabar audio del micrófono con JavaScript y descargar la grabación - Parzibyte's blog · mayo 20, 2019 a las 12:04 pm

[…] Nota: recuerda que ya vimos cómo tomar una foto con JavaScript y la cámara web para descargar, o para enviarla a PHP. […]

Codificar y decodificar base64 en JavaScript - Parzibyte's blog · septiembre 28, 2018 a las 1:00 pm

[…] Claro que no podemos únicamente convertir cadenas, de hecho podemos codificar, por ejemplo, una foto como vimos en este tutorial para tomar una foto con la webcam desde JavaScript. […]

Tomar foto de cámara web con Javascript, actualizado - Parzibyte's blog - El blog de Luis Cabrera · abril 4, 2018 a las 10:30 am

[…] es la versión 2 de la primera entrada que escribí. Estaba probando la app en Chrome, y aparecía un mensaje que dice […]

Tomar foto de webcam con Python - Parzibyte's blog - El blog de Luis Cabrera · marzo 20, 2018 a las 9:02 pm

[…] vimos cómo tomar una foto de la cámara web o de cualquier dispositivo utilizando Javascript. En aquel post, guardábamos la foto con PHP. Hoy veremos cómo tomar una foto con Python y […]

Tomar foto de cámara con Python y guardarla en servidor con PHP - Parzibyte's blog - El blog de Luis Cabrera · marzo 20, 2018 a las 8:59 pm

[…] de publicar cómo tomar una foto de la cámara web con Python. La vez pasada, vimos cómo tomar una foto con Javascript y guardarla en servidor con PHP. Lo hicimos porque con Javascript (al menos en el navegador, no con NodeJS) no podemos escribir […]

Deja un comentario

Marcador de posición del avatar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

A %d blogueros les gusta esto: