Tema representativo de entrevista

Entrevista Frontend: ¿Cómo usarías Blob.bytes() para cargas binarias con una alternativa (fallback) para navegadores antiguos?

FrontendIntermedio
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Debes cargar archivos grandes y calcular un resumen criptográfico (digest) en el navegador. Los navegadores nuevos admiten Blob.bytes(), pero los más antiguos tal vez no. Diseña la lectura, la división en fragmentos (chunking), la alternativa (fallback), la cancelación, el manejo de errores y la validación de compatibilidad.

Prompt y alcance

Debes cargar archivos grandes y calcular un resumen criptográfico (digest) en el navegador. Los navegadores nuevos admiten Blob.bytes(), pero los más antiguos tal vez no. Diseña la lectura, la división en fragmentos (chunking), la alternativa (fallback), la cancelación, el manejo de errores y la validación de compatibilidad.

MDN marca Blob.bytes() como Baseline 2026: devuelve una Promise que se resuelve en un Uint8Array que contiene los datos del Blob y está disponible en Web Workers. La entrevista evalúa la semántica de la API y los límites de recursos; simplemente admitir un nuevo método no constituye un diseño de carga completo.

Lo que evalúa el entrevistador

  • Describir con precisión el resultado de Promise y Uint8Array sin llamarlo flujo (stream).
  • Reconocer el pico de memoria al leer un Blob completo y diseñar fragmentos (chunks) o una ruta de transmisión (stream).
  • Usar detección de características con alternativas basadas en arrayBuffer() o stream() en lugar de comprobaciones por nombre de navegador.
  • Manejar la cancelación, los reintentos, el estado del digest, la mensajería de los workers y la retroalimentación al usuario.
  • Demostrar el diseño con una matriz de compatibilidad y pruebas de estrés con archivos grandes.

Preguntas de clarificación

  • ¿Cuáles son los límites de tamaño de archivo, la concurrencia de carga, los navegadores objetivo y la disponibilidad de Workers?
  • ¿Qué algoritmo de digest, protocolo de fragmentos en el servidor, semántica de reanudación y política de fragmentos duplicados se aplican?
  • ¿Debe existir un digest completo antes de la carga, o el cliente puede cargar mientras lee y verificar al final?
  • ¿Puede una transferencia fallida reanudarse desde los fragmentos confirmados y cómo se define la idempotencia del servidor?
  • ¿Qué sucede cuando un usuario navega a otra página, cierra la pestaña o el dispositivo está bajo presión de memoria?

Semántica de API y rutas de lectura

Blob.bytes() no recibe argumentos y devuelve una Promise. El valor cumplido es un Uint8Array; un error de lectura rechaza la Promise. Entrega el contenido del Blob a quien llama como un único arreglo de bytes, por lo que no garantiza un comportamiento de copia cero (zero-copy) ni una capacidad ilimitada para archivos grandes. Lee archivos pequeños en un Worker si resulta útil; para archivos grandes, prefiere fragmentos con slice() seguidos de la lectura y carga por fragmento.

Detecta la ruta mediante características: usa bytes() cuando esté disponible; de lo contrario, usa arrayBuffer() y crea un Uint8Array; cuando el navegador y el protocolo lo admitan, consume stream() de forma incremental. Las alternativas deben preservar la numeración de fragmentos, la entrada del digest y los contratos de error para que la validación del servidor no cambie con la API.

Memoria, fragmentos y resúmenes criptográficos

Leer un Blob completo genera un pico al menos del orden del tamaño del archivo, además de los búferes de carga, la decodificación y la sobrecarga del entorno de ejecución. Llama a slice(start, end) con límites fijos o adaptables y conserva únicamente el fragmento actual y una cola de carga acotada. Alimenta el digest en el orden del archivo; las cargas en paralelo no deben reordenar la entrada del digest.

Cada fragmento lleva un ID de archivo, versión, índice, longitud y el digest del contenido. El servidor almacena de manera idempotente por ID de archivo e índice, luego ensambla en orden y verifica la longitud total y el digest del archivo completo. Los resúmenes de los fragmentos no reemplazan a un digest de extremo a extremo, ya que la pérdida, el reordenamiento o una concatenación incorrecta aún pueden producir un archivo erróneo.

Cancelación, reintentos y workers

Pasa una señal AbortController a las lecturas y solicitudes de carga; la cancelación limpia los fragmentos en cola y libera las referencias. Reintenta únicamente los errores recuperables con retroceso exponencial (exponential backoff) y una clave de fragmento idempotente. La expiración de la autenticación debe pausar el proceso para reautorizar, no reintentar indefinidamente. Si un Worker calcula el digest, el hilo principal recibe el progreso, los errores y el resultado final en lugar de copiar todo el arreglo de bytes de vuelta.

Con stream(), respeta la contrapresión (backpressure): el lector no debe superar la velocidad de la red ni la del consumidor del digest. Cada ruta registra los fragmentos confirmados, las duraciones de lectura y carga, los reintentos, los motivos de cancelación y un pico de memoria estimado, sin registrar el contenido de los archivos ni rutas sensibles.

Compatibilidad y límites de seguridad

La detección de características puede comprobar typeof Blob !== "undefined" y "bytes" in Blob.prototype antes de seleccionar bytes(). No confíes únicamente en User-Agent. Los navegadores antiguos pueden recurrir a arrayBuffer() o a un FileReader controlado, y deben recibir un mensaje explícito cuando no se pueda cumplir con el presupuesto de memoria.

Los resúmenes del lado del cliente proporcionan evidencia de integridad, no autorización, análisis de malware ni validación del tipo de contenido. El servidor debe limitar el tamaño del archivo, el tamaño del fragmento, el rango de índices y la duración total; un tipo MIME proporcionado por el cliente no es una decisión de seguridad. Evita copiar datos sensibles entre workers y libera las referencias con prontitud tras la finalización.

Simulacros de fallas y lista de verificación de validación

Cubre navegadores con y sin bytes(), rutas en Worker y en el hilo principal, archivos vacíos, archivos de un solo fragmento y de múltiples fragmentos, archivos muy grandes, interrupción de la red, cancelar y reanudar, fragmentos duplicados, fragmentos fuera de orden y discrepancia de digest. Las pruebas de estrés registran el tiempo de lectura p95, el rendimiento de carga, el pico de memoria, las tareas largas (long tasks), la tasa de fallas y el tiempo de recuperación.

Si bytes() se rechaza, preserva el estado del fragmento y cambia a una alternativa explícita. Si la alternativa excede el presupuesto de memoria, detén el proceso con un mensaje de siguientes pasos en lugar de forzar una lectura completa. Si el servidor detecta una discrepancia en el digest del fragmento o del archivo completo, descarta el ensamblado no confirmado y reanuda desde el último fragmento consistente.

Preguntas de seguimiento y respuestas de referencia

¿Cómo eliges entre bytes() y arrayBuffer()?

Ambos pueden cargar el Blob completo en memoria. bytes() suministra directamente un Uint8Array, lo cual es útil para código orientado a bytes, pero no proporciona transmisión automáticamente. Los archivos grandes deben usar fragmentos o una ruta de transmisión.

¿Por qué es insuficiente un digest de archivo completo que solo reside en el cliente?

No puede probar que el servidor haya recibido cada fragmento en orden y no reemplaza la autorización ni las comprobaciones de seguridad del contenido. Verifica los índices de los fragmentos, los resúmenes de los fragmentos, la longitud total y el digest final del servidor.

¿Cómo validas la alternativa para navegadores antiguos?

Fuerza ambas rutas de capacidad mediante detección de características y luego prueba una matriz de navegadores reales, la ejecución en Worker y en el hilo principal, la presión de memoria, la recuperación sin conexión y la cancelación. Un único navegador moderno es insuficiente.

Fuentes públicas

Preguntas relacionadas