2
Help us understand the problem. What are the problem?

posted at

updated at

AWS Lambda + S3 + Python で PDF を扱う際のTips

初めに

PDF を AWS で扱う際に、ローカルの環境と LambdaS3 では少し扱いに注意が必要だったので、忘れないようにまとめておきます。

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/ 配下でないとエラーになります。

  • 実際のエラーログ error.png

他のディレクトリを指定すると、このようなエラーになります。

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)で表示する場合

クライアント側では、変換した後に PDFKitPDFDocument に 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つずつクリアしていくのが苦労しました。また、知見が増えたら追加していこうと思います。

参考文献

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
2
Help us understand the problem. What are the problem?