🙌
PDF を Web API で Base64 転送して HTML に埋め込む
Web 系開発の話です。
バックエンドサーバ上にある PDF ファイル を Web API 経由でフロントエンドに転送して埋め込み表示する方法を試したため本稿にメモしておきます。
環境
- next.js 12.3.0
- react 18.2.0
構成
バックエンド側
- サーバ側の PDF ファイルのバイナリを Base64 に変換して payload に乗せて送出します。
/pages/api/pdf.ts
import { readFile } from 'fs'
import type { NextApiRequest, NextApiResponse } from 'next'
import path from 'path'
type Data = {
name: string
data: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
readFile(path.join(process.cwd(), '/file/embed.pdf'), null, (err, data)=>{
res.status(200).json({
name: 'embed.pdf',
data: data.toString('base64')
})
})
}
フロントエンド側
試して良さそうと思った方法。
- GET リクエストで取得した payload から
- Data URL
data:application/pdf;base64,(base64文字列)
で直接埋め込み - Blob 変換して Object URL を作成してから埋め込み
- Data URL
埋め込み先は Embed でも object でも iframe でも問題ありません。
詳しくは説明しませんが、データをダウンロードする場合は、直接 Base64 を埋め込むケースと Object URL を経由するケースでダウンロードファイル名の挙動が異なります。
/pages/index.tsx
import type { NextPage } from 'next';
import { useEffect, useRef } from 'react';
import styles from '../styles/Home.module.css';
const Home: NextPage = () => {
const file = useRef<string>()
const embed = useRef<HTMLEmbedElement>(null)
const iframe = useRef<HTMLIFrameElement>(null)
const open = useRef<HTMLAnchorElement>(null)
const download = useRef<HTMLAnchorElement>(null)
useEffect(()=>{
fetch('/api/pdf').then(res=>res.json()).then(json=>{
// バイナリを Embed する方法
if(embed.current){
embed.current.type = 'application/pdf'
embed.current.src=`data:application/pdf;base64,${json.data}#toolbar=0&navpanes=0`
}
// Blob URL に変換して参照させる方法
fetch(`data:application/pdf;base64,${json.data}`).then(res=>res.blob()).then(blob=>{
const name = json.name
file.current = URL.createObjectURL(blob) // 一時 URL を作成
if(iframe.current){
iframe.current.src = `${file.current}#toolbar=0&navpanes=0`
}
if(open.current){
open.current.href = file.current
}
if(download.current){
download.current.href = file.current
download.current.download = name // ダウンロード指示 & ファイル名をサジェスト
}
})
})
// cleanup
return ()=>{
if(file.current) {
URL.revokeObjectURL(file.current) // URL を解放 (メモリリークも防止)
}
}
},[])
return (
<main className={styles.main}>
<div><embed ref={embed} width={800} height={800}/></div>
<div><iframe ref={iframe} width={800} height={800}/></div>
<div><a ref={open}>開く</a></div>
<div><a ref={download}>ダウンロード</a></div>
</main>
)
}
export default Home
結果
Html に埋め込み表示できました。
ソースファイル
Discussion