feat: static server support etag

This commit is contained in:
2025-06-18 04:42:33 +08:00
parent cc06142050
commit c12b9b360a
7 changed files with 114 additions and 17 deletions

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env zx
import { glob } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { chunk } from 'es-toolkit/array';
const dataDir = path.join(import.meta.dirname, '../../../data')
/**
* @type {string[]}
*/
const images = [];
for await (const image of glob('**/*.{jpg,jpeg,png,gif,svg}', {
cwd: dataDir,
})) {
images.push(image)
}
const cpus = os.cpus().length - 1;
const chunkSize = Math.ceil(images.length / cpus);
const chunks = chunk(images, chunkSize);
/**
* @param {string[]} images
*/
async function convertImages(images) {
for await (const image of images) {
const imagePath = path.resolve(dataDir, image)
const webp = imagePath.replace(path.extname(imagePath), '.webp')
const avif = imagePath.replace(path.extname(imagePath), '.avif')
console.log(`Converting ${imagePath} to ${webp}...`);
await $`ffmpeg -i "${imagePath}" -c:v libwebp -lossless 1 "${webp}"`;
console.log(`Converting ${imagePath} to ${avif}...`);
await $`ffmpeg -i "${imagePath}" -c:v libaom-av1 -still-picture 1 -pix_fmt yuv420p10le -crf 0 -strict experimental "${avif}"`;
}
}
await Promise.all(
chunks.map(convertImages)
)

View File

@@ -1,4 +1,4 @@
use std::fmt;
use std::{borrow::Cow, fmt};
use async_stream::try_stream;
use axum::{body::Body, response::Response};
@@ -206,6 +206,12 @@ impl StorageService {
let mime_type = mime_guess::from_path(storage_path.as_ref()).first_or_octet_stream();
let content_type = HeaderValue::from_str(mime_type.as_ref())?;
let etag = metadata.etag().map(Cow::Borrowed).or_else(|| {
let len = metadata.content_length();
let lm = metadata.last_modified()?.timestamp();
Some(Cow::Owned(format!("\"{lm:x}-{len:x}\"")))
});
let last_modified = metadata.last_modified().map(|lm| lm.to_rfc2822());
let response = if let Some(TypedHeader(range)) = range {
let ranges = range
@@ -240,7 +246,7 @@ impl StorageService {
};
let body = Body::from_stream(stream);
Response::builder()
let mut builder = Response::builder()
.status(StatusCode::PARTIAL_CONTENT)
.header(
header::CONTENT_TYPE,
@@ -248,17 +254,34 @@ impl StorageService {
format!("multipart/byteranges; boundary={boundary}").as_str(),
)
.unwrap(),
)
.body(body)?
);
if let Some(etag) = etag {
builder = builder.header(header::ETAG, etag.to_string());
}
if let Some(last_modified) = last_modified {
builder = builder.header(header::LAST_MODIFIED, last_modified);
}
builder.body(body)?
} else if let Some((r, content_range)) = ranges.pop() {
let reader = self.reader(storage_path.as_ref()).await?;
let stream = reader.into_bytes_stream(r).await?;
Response::builder()
let mut builder = Response::builder()
.status(StatusCode::PARTIAL_CONTENT)
.header(header::CONTENT_TYPE, content_type.clone())
.header(header::CONTENT_RANGE, content_range)
.body(Body::from_stream(stream))?
.header(header::CONTENT_RANGE, content_range);
if let Some(etag) = metadata.etag() {
builder = builder.header(header::ETAG, etag);
}
if let Some(last_modified) = last_modified {
builder = builder.header(header::LAST_MODIFIED, last_modified);
}
builder.body(Body::from_stream(stream))?
} else {
unreachable!("ranges length should be greater than 0")
}
@@ -276,10 +299,19 @@ impl StorageService {
let reader = self.reader(storage_path.as_ref()).await?;
let stream = reader.into_bytes_stream(..).await?;
Response::builder()
let mut builder = Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, content_type)
.body(Body::from_stream(stream))?
.header(header::CONTENT_TYPE, content_type);
if let Some(etag) = etag {
builder = builder.header(header::ETAG, etag.to_string());
}
if let Some(last_modified) = last_modified {
builder = builder.header(header::LAST_MODIFIED, last_modified);
}
builder.body(Body::from_stream(stream))?
};
Ok(response)