Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SSRで動的に
OGP画像を生成したい!
〜Cloudflare Workersから@vercel/og移行編〜

jiko21
June 14, 2023
89

SSRで動的に
OGP画像を生成したい!
〜Cloudflare Workersから@vercel/og移行編〜

KFUG Web Creators Meetup #2の資料です

jiko21

June 14, 2023
Tweet

Transcript

  1. OGP

  2. ଎͘͢Δʹ͸(࠶ܝ) • ίʔϧυελʔτͳ͍ํ͕خ͍͠! • ίʔϧυελʔτͪ͠Ό͏ͱͲ͏ͯ͠΋… 
 㱺 (Cloud fl are

    workersͩͱ)ϗοτελʔτ • ͳΔ͚ͩϢʔβʔ͔Β͍ۙͱ͜ΖͰ • ౦ژϦʔδϣϯͱ͔͡Όͳͯ͘΋ͬͱۙ͘ʹ! 
 㱺·͞ʹͦ͏(CDNͳͷͰ)
  3. Cloud fl are WorkersͬͯԿ? • CDN࡞ͬͯΔCloud fl are͕΍ͬͯΔEdge WorkerͷαʔϏεج൫ •

    Nodeͱ͔Wasm͕Edge(CDN)্Ͱಈ͘ʂ • ҎԼ஫ҙ • NodeͷAPIͱ͔ݺͼग़ͤΔΘ͚͡Όͳ͍ • ϑΝΠϧαΠζʹ্ݶ(ѹॖޙͰධՁ͞ΕΔ͚Ͳ)͋Γ
  4. Rust(Web AssemblyͰॻ͍ͨ fn get_params(text: String) -> Vec<[u8; 3]> { let

    mut rslt: Vec<[u8; 3]> = vec![]; let re = Regex::new(r"color=%23([0-9A-Fa-f] {6})").unwrap(); for mat in re.captures_iter(&text) { let rgb_string = mat.get(1).map_or("", |m| m.as_str()).trim().to_string(); let mut decoded = [0; 3]; hex::decode_to_slice(rgb_string, &mut decoded).expect("Decoding failed"); rslt.push(decoded) } rslt } fn gen_img(colors: &Vec<[u8; 3]>) -> Vec<u8> { let mut img: RgbImage = ImageBuffer::new(1200, 630); let color_size = colors.len() as u32; let each_size = 1200 / color_size; for (x, _y, pixel) in img.enumerate_pixels_mut() { let cursor = (x / each_size) as usize; *pixel = image::Rgb(*colors.get(cursor).unwrap()); } let mut img_bytes: Vec<u8> = Vec::new(); img.write_to(&mut Cursor::new(&mut img_bytes), image::ImageOutputFormat::Png).unwrap(); img_bytes } async fn hundle_ogp(colors: &Vec<[u8; 3]>) -> Result<Response> { let img_bytes = gen_img(colors); let mut resp = Response::from_bytes(img_bytes)?; resp.headers_mut().set("content-type", "image/png")?; Ok(resp) } #[event(fetch)] pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> { utils::set_panic_hook(); let router = Router::new(); router .get_async("/", |req, _| async move { let url = req.url().unwrap(); if let Some(query_params) = req.url()?.query() { let params = query_params; hundle_ogp(&get_params(params.to_string())).await } else { let colors: Vec<[u8; 3]> = vec![[0, 0, 255], [0, 255, 0], [255, 255, 0], [239, 129, 15], [255, 0, 0]]; hundle_ogp(&colors).await } }) .run(req, env) .await }
  5. @vercel/ogͰॻ͍ͨ export default function (req: NextRequest, res: NextResponse) { const

    paramColors = new URLSearchParams(req.nextUrl.search).getAll('color'); const colors = paramColors.length > 0 ? paramColors : ['#0000FF', '#00FF00', '#FFFF00', '#F0810F', '#FF0000'] return new ImageResponse( ( <div style={{ fontSize: 128, width: '100%', height: '100%', display: 'flex', textAlign: 'center', alignItems: 'center', justifyContent: 'center', }} > {colors.map((color) => (<div style={{ height: '100%', width: '100%', background: `${color}`, color: `${color}`, } } key={color}> </div>))} </div> ), { width: 1200, height: 630, }, ); } $44ࡶʹॻ͍ͨΒ 
 0(ੜ੒Ͱ͖Δͷ ͸Α͍