LoginSignup
11
18

More than 5 years have passed since last update.

JavaScriptでPDFのサムネイル画像を作成しアップロードする

Last updated at Posted at 2017-12-17

JavaScriptで画像をリサイズしてから画像をアップロードする

以前作成した上記のものにPDF対応にしたものを作成してみました。

  • PDF.jsを使用しています。
  • サーバーサイドの処理は前回から変更していません。

サーバーサイドは無いので失敗しますが、以下のような感じになります。
(CSRFの処理はコメントアウトしています。)
https://jsfiddle.net/bfb4b39z/41/

コメントで指摘いただいたものを参考に更新しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
<script type="text/javascript" src="https://mozilla.github.io/pdf.js/build/pdf.worker.js"></script>
<script>
let scaleSlider = null;
let scaleValue = null;
let canvas = null;
let context = null;
let image = null;
const reader = new FileReader();
let fixFileObject = null;
let scale = null;
let messageArea = null;
let pdfFlag = false;
const pdfImageExtention = "png";
const csrf_token_name = "{$csrf_token_name}";
let csrf_hash = "{$csrf_hash}";

document.addEventListener("DOMContentLoaded", function() {
    canvas = document.getElementById("thumbnail");
    context = canvas.getContext("2d");

    scaleSlider = document.getElementById("scale");
    scaleValue = document.getElementById("scale-value");

    messageArea = document.getElementById("message")

    scaleSlider.addEventListener("change", changeScale);

    setFileEventListenner();
});

function changeScale() {
    scaleValue.value = scaleSlider.value;
    drawCanvas();
}

function setFileEventListenner() {
    document.getElementById("file-selecter").addEventListener("change", createPreview);
}

function createPreview(event) {

    fixFileObject = null;

    let fileObject = event.target.files[0];

    if (typeof fileObject === "undefined") {
        return;
    }

    if (fileObject.type.match(/^(image\/jpeg|image\/png|application\/pdf)$/) === null) {
        // jpegとpngとpdf以外の場合はクリアして終了
        let fileArea = document.getElementById("file-input");
        fileArea.innerHTML = fileArea.innerHTML;
        setFileEventListenner();
        return;
    }

    pdfFlag = fileObject.type.match(/^application\/pdf$/) !== null;

    fixFileObject = fileObject;

    image = new Image();

    reader.onload = function(event) {
        if ( ! pdfFlag) {
            image.src = event.target.result;// base64
        } else {
          pdfjsLib.getDocument(new Uint8Array(event.target.result))
                .then(pdf => {
                    return pdf.getPage(1);
                })
                .then(page => {
                    canvas.style.display = "none";// 読み込み時に下記のサイズで描画されるのでdrawCanvas()まで非表示にしておく
                    // ここで基準となるcanvasサイズが決まるのでscaleは1で読み込んでおく
                    let viewport = page.getViewport(1);
                    canvas.height = viewport.height;
                    canvas.width = viewport.width;

                    let renderContext = {
                        canvasContext: context,
                        viewport: viewport
                    };

                    // Render PDF page and set image
                    return page.render(renderContext);// Promise
                })
                .then(() => {
                    return new Promise(resolve => {
                        canvas.toBlob(blob => {
                            resolve(blob);
                        }, "image/" + pdfImageExtention);
                    });
                })
                .then(blob => {
                    if(image.src) {
                        const oldSrc = image.src;
                        URL.revokeObjectURL(oldSrc);// createObjectURLで作成したURLを削除
                    }
                    image.src = URL.createObjectURL(blob);
                })
                .catch(error => {
                    messageArea.innerHTML = '<span style="color: red;">ファイルの読み込みに失敗しました。</span>';
                });
        }
    }

    image.onload = function() {
        drawCanvas();
    }

    pdfFlag ? reader.readAsArrayBuffer(fileObject) : reader.readAsDataURL(fileObject);
}

function drawCanvas() {
    scale = scaleSlider.value;
    if (image !== null) {
        let imageWidth = parseInt(image.width * scale, 10);
        let imageHeight = parseInt(image.height * scale, 10);
        canvas.width = imageWidth;
        canvas.height = imageHeight;
        context.clearRect(0, 0, imageWidth, imageHeight);
        context.drawImage(image, 0, 0, imageWidth, imageHeight);
    }
    if (canvas.style.display === "none")
    {
        canvas.style.display = "inherit";
    }
}

// awaitを使うのでasync宣言
async function submitResizeFile() {
    if (image !== null && fixFileObject !== null) {

        let fileType = pdfFlag ? "image/" + pdfImageExtention : fixFileObject.type;
        let fileName = pdfFlag ? fixFileObject.name.replace(/pdf/g, pdfImageExtention) : fixFileObject.name;

        // toBlobが非同期処理なのでawaitでPromise待ち
        let resizeFileObject = await (() => {
            return new Promise(resolve => {
                if (scale != 1 || pdfFlag === true) {
                    // 画像ファイルでサイズ変更があった場合またはPDFファイルの場合に送信用ファイルを作成
                    canvas.toBlob(blob => {
                        resolve(blob);
                    }, fileType);
                } else {
                    // 画像ファイルでサイズ変更がない場合はそのまま
                    resolve(fixFileObject);
                }
            });
        })();

        let formData = new FormData();
        formData.append(csrf_token_name, csrf_hash);
        formData.append("file", resizeFileObject, fileName);

        // input type file の 値はjavascriptで上書き出来ないのでajaxで送信する
        fetch("/root/execute_upload", {
            method: "POST",
            body: formData,
            credentials: "same-origin",// send Cookie csrf_token
        }).then(response => {
            // Forbidden is not json
            if (response.status !== 403) {
                response.json().then(function(json) {
                    csrf_hash = json.csrf_hash;
                });
            }
            if (response.ok) {
                messageArea.innerHTML = '<span style="color: green;">ファイルの送信に成功しました。</span>';
            } else {
                messageArea.innerHTML = '<span style="color: red;">ファイルの送信に失敗しました。</span>';
            }
        });
    }
}
</script>
</head>
<body>
<form id="form">
    Scale : <input id="scale" type="range" step="0.1" min="0.1" max="1" value="1"><input disabled id="scale-value" type="text" value="1" style="width: 2rem;">
    <div id="file-input"><input id="file-selecter" type="file" name="file" accept="image/jpeg, image/png, application/pdf"></div>
    <div><canvas id="thumbnail" style="display: none;"></canvas></div>
    <input type="button" value="送信" onclick="submitResizeFile()">
    <div id="message"></div>
</form>
</body>
</html>
11
18
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
18