初めに
PDF を AWS
で扱う際に、ローカルの環境と Lambda
や S3
では少し扱いに注意が必要だったので、忘れないようにまとめておきます。
1. S3 から PDF を読み込む
Python
で PDF を扱う場合は PyPDF2
を使うことが多いかと思います。
ローカル環境で読み込む場合は
# example code
from PyPDF2 import PdfFileReader
PDF_PATH = 'pdf/xxx.pdf' # ローカルのディレクトリ
pdf = PdfFileReader(PDF_PATH)
とパスを指定するだけで簡単に読み込むことができます。
S3
から PDF を読み込む場合は少し工夫が必要です。
# example code
import boto3
from PyPDF2 import PdfFileReader
from io import BytesIO
PDF_PATH = 'data/pdf/xxx.pdf' # S3バケットのディレクトリ
BUCKET_NAME = 'your-bucket-name'
bucket = boto3.resource('s3').Bucket(BUCKET_NAME)
pdf_obj = bucket.Object(path).get()['Body'].read() # S3からの取り出し
bytes = BytesIO(pdf_obj) # 変換する
pdf = PdfFileReader(bytes)
バケットからただ読み込むだけでは機能せず、BytesIO
で 変換することで扱うことができます。
2. Lambda上で PDF の一時編集する
これは PDF に限らずですが、Lambda
で書き込む際は /tmp/
配下でないとエラーになります。
他のディレクトリを指定すると、このようなエラーになります。
3. Lambda から PDF ファイルを取得する
API として PDF を返却する場合があると思います。
# example code
with open(output_file, 'rb') as data:
pdf = data.read()
PDF を開いて Lambda
上でクライアント側に return
した場合、自動で utf-8
に変換されるようで、日本語など無効な文字があるとエラーが発生します。
[ERROR] UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80
そこで、base64
で変換します。そうすることでエラーを回避することができます。
# example code
import base64
with open(output_file, 'rb') as data:
pdf = data.read()
return base64.b64encode(pdf).decode('utf-8')
当然ながらクライアント側でも対応が必要になるので注意してください。
HTMLで表示する場合
デコードは必要ないですが、特定の書き方が必要です。
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body style="height: 100%; width: 100%; overflow: hidden; margin:0px; background-color: rgb(82, 86, 89);">
<embed style="position:absolute; left: 0; top: 0;" width="100%" height="100%" type="application/pdf"
src="data:application/pdf;base64,/* base64 の文字列を入れる */" />
</body>
</html>
(引用:ブラウザでbase64エンコードしたPDFファイルを表示する)
iOS(Swift)で表示する場合
クライアント側では、変換した後に PDFKit
の PDFDocument
に Data型でセットします。
import PDFKit
let pdf = PDFView()
pdf.autoScales = true
pdf.displayMode = .singlePageContinuous
// 変換する
if let data = Data(base64Encoded: str) {
pdfView.document = .init(data: data)
}
pdfView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pdfView)
NSLayoutConstraint.activate([
pdfView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
pdfView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
pdfView.topAnchor.constraint(equalTo: view.topAnchor),
pdfView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
終わりに
Lambda
上で実行しないと分からないエラーがそれなりにあるので、1つずつクリアしていくのが苦労しました。また、知見が増えたら追加していこうと思います。
参考文献