Hola Habr!
Estoy haciendo un sitio web para mi proyecto. En el sitio, debe mostrar muchos gifs al mismo tiempo, cada uno pesa bastante bien. Si muestra todo a la vez, la página se carga durante mucho tiempo. Al mismo tiempo, también es imposible dar la página sin GIF (para que puedan descargarse allí).
A cualquiera que esté interesado en cómo me ocupé de este problema, le pido un gato.
En realidad, el problema
Como dije, hay MUCHOS gifs en el sitio, buenos y diferentes. Puedes ver por ti mismo cuántos de ellos:
reface.tech
Tan pronto como vi la versión beta del aterrizaje, inmediatamente tuve un problema con la carga: para algunos usuarios, las páginas se estaban cargando durante mucho tiempo. Para un negocio serio, este, por supuesto, no es el caso.
Era necesario resolver esto de alguna manera. Era imposible sobrevivir con una simple compresión sin pérdidas, ya que en ese momento todo el contenido de los medios ya había sido conducido a través de
compressor.io . Por lo tanto, era necesario comprimir con pérdidas y dar GIF con peor calidad.
Pero distribuir contenido multimedia malo a usuarios con buena Internet es una especie de sacrilegio. Por lo tanto, se decidió determinar de alguna manera la velocidad de estos de su Internet, y dependiendo de la velocidad para obtener la calidad adecuada.
Hacemos un bosquejo aproximado
Tendremos una matriz con una descripción del contenido multimedia a cargar.
Un ejemplo:
[ { "large": { "size": 0.6211118698120117, "url": "gifs/control/large/control_1.gif" }, "middle": { "size": 0.5330495834350586, "url": "gifs/control/middle/control_1.gif" }, "small": { "size": 0.4901447296142578, "url": "gifs/control/small/control_1.gif" } } ]
Aquí en los elementos para el grado apropiado de calidad se encuentra la url y el tamaño del archivo.
Nosotros simplemente:
- Ir a través de la matriz (al cargar la página)
- Lea el tamaño total cargado para cada grado de calidad
- Determine si se cargará todo en 4 segundos (este es el período que personalmente me molesta)
En consecuencia, es necesario:
- Vi algún tipo de cosa que hará una matriz de este tipo para que todo no cuente con bolígrafos (en python)
- Vio la velocidad de internet
Aserrar el medidor de velocidad
Será relativamente simple. Escribimos en la barra de direcciones de su navegador
eu.httpbin.org/stream-bytes/51200 . Se descarga un archivo con una longitud de 51200 bytes. Lo empujamos a nosotros mismos en público (para medir la velocidad de un hosting).
Ahora tenemos que verificar cuánto oscila el archivo. Archivaremos una función simple para esto, que nos devolverá la velocidad en megabytes por segundo.
async checkDownloadSpeed(baseUrl, fileSizeInBytes) { return new Promise((resolve, _) => { let startTime = new Date().getTime(); return axios.get(baseUrl).then( response => { const endTime = new Date().getTime(); const duration = (endTime - startTime) / 1000; const bytesPerSecond = (fileSizeInBytes / duration); const megabytesPerSecond = (bytesPerSecond / 1000 / 1000); resolve(megabytesPerSecond); }); }).catch(error => { throw new Error(error); }); }
Es decir, registramos el tiempo de inicio de la descarga, el tiempo de finalización, medimos la diferencia y, dado que conocemos el tamaño del archivo, simplemente lo dividimos.
Ahora escribiremos una función que hará algo a esta velocidad particular:
async getNetworkDownloadSpeed() { const baseUrl = process.env.PUBLIC_URL + '/51200'; const fileSize = 51200; const speed = await this.checkDownloadSpeed(baseUrl, fileSize); console.log("Network speed: " + speed); if (speed.mbps === "Infinity") { SpeedMeasure.speed = 1; } else { SpeedMeasure.speed = speed * 5; } }
En general, hay un problema con este código: es que la velocidad de conexión exacta no se determina debido al pequeño tamaño del archivo. Pero descargar algo más es demasiado costoso para nosotros, por lo que simplemente multiplicamos la velocidad resultante por 5. Incluso con un margen, lo tomamos, porque en realidad la velocidad será aún mayor.
Ahora archivaremos una función que, dependiendo de la velocidad, generará la calidad correspondiente:
static getResolution(gifsArray) { let totalSizeLevel1 = 0; let totalSizeLevel2 = 0; let totalSizeLevel3 = 0; for (let i = 0; i < gifsArray.length; i++) { for (let a = 0; a < gifsArray[i].length; a++) { let element = gifsArray[i][a]; totalSizeLevel1 += element.small.size; totalSizeLevel2 += element.middle.size; totalSizeLevel3 += element.large.size; } } if (isNaN(SpeedMeasure.speed)) { SpeedMeasure.speed = 1; } let timeLevel1 = totalSizeLevel1 / SpeedMeasure.speed; let timeLevel2 = totalSizeLevel2 / SpeedMeasure.speed; let timeLevel3 = totalSizeLevel3 / SpeedMeasure.speed; if (timeLevel3 < APPROPRIATE_TIME_LIMIT) { return "large"; } else if (timeLevel2 < APPROPRIATE_TIME_LIMIT) { return "middle"; } else { return "small"; } }
Dado que la función que cuenta la velocidad es asíncrona para nosotros, SpeedMeasure.speed puede ser cero. Por defecto, creemos que la velocidad de conexión es de 1 megabyte por segundo. Cuando la función calcula la velocidad, simplemente volvemos a renderizar el contenedor.
Pasamos una matriz de matrices a la función getResolution. Por qué Porque si tenemos varios contenedores en la página, es más conveniente para nosotros transferir el contenido correspondiente a cada uno de ellos en una matriz, pero debemos considerar la velocidad de descarga de una vez.
Ejemplo de uso
Aquí hay un ejemplo de uso (en React):
async runFunction() { let speedMeasure = new SpeedMeasure(); await speedMeasure.getNetworkDownloadSpeed(); this.forceUpdate() } componentDidMount() { this.runFunction(); } render() { let quality = SpeedMeasure.getResolution([ Control.getControlArray(), Health.getHealthArray() ]); return ( <div className="app"> <Presentation /> <Control quality={quality} /> <Health quality={quality} /> </div> ); }
En consecuencia, cuando se carga nuestra determinación de velocidad, el contenedor se procesará nuevamente.
Dentro del contenedor (por ejemplo, dentro de Control), simplemente toma el GIF correspondiente de la matriz (por índice), luego obtiene un objeto por calidad y obtiene una referencia por url. Todo es simple
Escribir un script de Python
Ahora tenemos que comprimir automáticamente los gifs y generar la matriz con la descripción del contenido.
Para comenzar, escribamos un script para la compresión. Usaremos gifsicle. Para contenido de compresión media, el nivel de compresión será 80 (de 200), para una característica muy poderosa será 160.
import os GIFS_DIR = "/home/mixeden//Landingv2/" COMPRESSOR_DIR = "/home/mixeden//gifsicle-static" NOT_OPTIMIZED_DIR = "not_optimized" OPTIMIZED_DIR = "optimized" GIF_RESIZED_DIR = "gif_not_optimized_resized" GIF_COMPRESSED_DIR = "gif_compressed" COMPRESSION_TYPE = ["middle", "small"] for (root, dirs, files) in os.walk(GIFS_DIR, topdown=True): if len(files) > 0 and GIF_RESIZED_DIR in root: for file in files: path = root + "/" + file for compression in COMPRESSION_TYPE: final_path = path.replace(GIF_RESIZED_DIR, GIF_COMPRESSED_DIR + "/" + compression + "/" + OPTIMIZED_DIR) print(path, final_path) if compression == COMPRESSION_TYPE[0]: rate = 80 else: rate = 160 os.system("echo 0 > " + final_path) os.system(COMPRESSOR_DIR + " -O3 --lossy={} -o {} {}".format(rate, final_path, path))
Para que las rutas a los archivos sean comprensibles, aquí está la descripción del directorio de archivos con gifs:
- NOT_OPTIMIZED_DIR - GIF no optimizados
- GIF_RESIZED_DIR: GIF no optimizados, pero redimensionados de acuerdo con el tamaño del contenedor para ellos
- GIF_COMPRESSED_DIR - GIF comprimidos
Dentro de las carpetas hay carpetas con nombres de categoría GIF. Dentro de una carpeta con una carpeta específica contiene "grande", "medio" y "pequeño" (debajo de los tipos de archivos comprimidos).
En el script, revisamos el directorio con gifs y para cada archivo hacemos dos tipos de compresión, llamando al comando de línea de comando correspondiente.
Ahora pasemos al script para crear una matriz con información.
import json import os GIFS_DIR = "/home/mixeden//Landingv2/" COMPRESSOR_DIR = "/home/mixeden//gifsicle-static" NOT_OPTIMIZED_DIR = "not_optimized" OPTIMIZED_DIR = "optimized" GIF_RESIZED_DIR = "gif_not_optimized_resized" GIF_COMPRESSED_DIR = "gif_compressed" COMPRESSION_TYPE = ["large", "middle", "small"] OUTPUT = {} for (root, dirs, files) in os.walk(GIFS_DIR, topdown=True): if len(files) > 0 and GIF_COMPRESSED_DIR in root and NOT_OPTIMIZED_DIR not in root: files.sort() type = root.split(GIFS_DIR)[1].split(GIF_COMPRESSED_DIR)[0].replace("/", "") print(type) if type not in OUTPUT: OUTPUT[type] = [] if len(OUTPUT[type]) == 0: for file in files: OUTPUT[type].append( { "large": { "url": "", "size": 0 }, "middle": { "url": "", "size": 0 }, "small": { "url": "", "size": 0 } }) for file in files: full_path = root + "/" + file bytes_size = os.path.getsize(full_path) kilobytes_size = bytes_size / 1000 megabytes_size = kilobytes_size / 1000 index = int(file.split("_")[1].replace(".gif", "")) - 1 for typer in COMPRESSION_TYPE: if typer in root: local_type = typer new_url = "gifs/" + full_path.replace(GIFS_DIR, "").replace("/" + GIF_COMPRESSED_DIR, "").replace("/" + OPTIMIZED_DIR, "") OUTPUT[type][index][local_type]['url'] = new_url OUTPUT[type][index][local_type]['size'] = megabytes_size print(OUTPUT) print(json.dumps(OUTPUT, indent=4, sort_keys=True))
Aquí revisamos las carpetas con los archivos, determinamos el tamaño del archivo, descubrimos el tipo de compresión y colocamos la información general en la matriz de SALIDA. Y luego enviamos esta matriz a la consola para copiarla.
Conclusión
Espero que este artículo te ayude con algo. Que tengan una buena codificación, muchachos.