Nuxtでネイティブカメラアプリを起動したい!
自己紹介
こんにちは、駆け出し web エンジニアの柿です!
現在は SES 企業で働いていて、主にフロントエンドの開発を行っています。
経験的にもまだ 1 年経っていないので、まだまだ勉強中ですが、よろしくお願いいたします mm
はじめに
今回は、Nuxt で画像を「撮影」もしくは「選択」することができて、かつその画像を API に送信する機能を実装してみました!
この機能を使う想定としては、
- 画像データを外部 API に送りつけてやりたい!
- 撮影されたデータや選択されたデータを保存したい!
なんて場面で使えるのではないかと思います。
では早速使った技術を軽く見ていきましょう。
使用技術
"devDependencies": {
"@nuxt/devtools": "latest",
"nuxt": "^3.8.0",
"sass": "^1.69.5",
"vue": "^3.3.7",
"vue-router": "^4.2.5"
}
上記のバージョンを使って作成しました。
$ node -v
v18.15.0
node のバージョンは上記の通りです。
Vue は composition API で書いています!
実装
今回使用するファイルは
- app.vue
- server/api/uploader.ts
の 2 つになります。
この辺は皆さんのプロジェクトの構成に合わせて柔軟に変更していただければと思います。
私の場合は、
$ npx nuxi@latest init shooting-sample
として始めたばっかなので、app.vue
にベタ書きです mm
app.vue
では先にクライアント側を用意していきましょう!
<template>
<div class="input-area">
<label for="upload">写真を撮影・選択する</label>
<input
id="upload"
type="file"
name="image"
accept="image/*"
/>
</div>
</template>
一旦こんな感じで用意しておきます。
accept="image/*"
で画像ファイルのみを選択できるようにするために指定しています。
ローカルで確認してみると、
写真を撮影・選択するファイルを選択
選択されていません
みたいな感じで表示されると思います。
「写真を撮影・選択する」以降は不要なので、見えなくしたいと思います!
<style scoped lang="scss">
.input-area > input {
display: none;
}
</style>
これで見えなくなったと思います!
display: none;
で非表示にしているだけなので、input
自体は機能します!
では次に、input
にファイルが選択された時に発火するイベントを設定していきます!
<template>
<div class="input-area">
<label for="upload">写真を撮影・選択する</label>
<input
ref="uploadFile"
type="file"
name="image"
id="upload"
accept="image/*"
@change="selectedFile"
/>
</div>
</template>
ref="uploadFile"
と@change="selectedFile"
を追加しています。
次に関数を用意してあげます!
<script setup lang="ts">
const uploadFile = ref<HTMLInputElement | null>(null);
const selectedFile = async () => {
console.log(uploadFile.value?.files);
};
</script>
これでファイルを追加したときに、コンソールに選択されたファイルの情報が表示されると思います!
実際に操作をしてみてください!
yarn dev --host
とすると、IP アドレス付きの URL で開発サーバーを立ち上げることができるので、同じ Wi-Fi に繋がっているスマホがあれば、スマホからも開発サーバーにアクセスすることができます!
では関数の中身をもっと充実させていきましょう!
const selectedFile = async () => {
const files = uploadFile.value?.files;
if (!files) {
console.error('No files selected');
return;
}
const file = files[0];
const fileData = new FormData();
fileData.append('image', file);
const { data: resData } = await useFetch('/api/uploader', {
method: 'POST',
body: fileData,
});
// NOTE: API実行後の結果確認用
console.log(resData.value);
};
という感じにしました!
ファイルのデータをFormData
という形式に変換してから、useFetch
を使ってserver/api/uploader.ts
に対してfileData
を POST しています!
app.vue
の方はこれで終わりです!
全体のコードはこちら
<script setup lang="ts">
const uploadFile = ref<HTMLInputElement | null>(null);
const selectedFile = async () => {
const files = uploadFile.value?.files;
if (!files) {
console.error('No files selected');
return;
}
const file = files[0];
const fileData = new FormData();
fileData.append('image', file);
const { data: resData } = await useFetch('/api/uploader', {
method: 'POST',
body: fileData,
});
// NOTE: API実行後の結果確認用
console.log(resData.value);
};
</script>
<template>
<div class="input-area">
<label for="upload">写真を撮影・選択する</label>
<input
ref="uploadFile"
type="file"
name="image"
id="upload"
accept="image/*"
@change="selectedFile"
/>
</div>
</template>
<style scoped lang="scss">
.input-area > input {
display: none;
}
</style>
それでは API 側の処理も書いていきます!
server/api/uploader.ts
server/api/uploader.ts
を作成したら、以下のように書いていきます!
import { writeFile } from 'fs/promises';
export default defineEventHandler(async event => {
const files = await readMultipartFormData(event);
if (!files || !files.length) {
throw createError({
statusCode: 400,
statusMessage: 'image not found',
});
} else if (files[0].name === 'image' && files[0].filename) {
const { filename, data } = files[0];
const filePath = `public/${filename}`;
await writeFile(filePath, data);
return {
message: 'success',
};
} else {
return {
message: "It's not image",
};
}
});
event
にはapp.vue
から送られてきたfileData
が入っています。
readMultipartFormData
を使うことで、FormData
形式のデータを変換してくれるようです。
files
には、
[
{
name: 'image',
filename: 'ファイル名.png',
type: 'image/png',
data: <Buffer ... 29523 more bytes>
}
]
といったイメージでデータが入っていると思います。
name
はapp.vue
でfileData.append('image', file);
としているので、image
となっています。
filename
は選択されたファイルの名前、
type
は選択されたファイルの種類、
data
は選択されたファイルのデータです。バイナリデータとかですかね、、?
await writeFile(filePath, data);
で、public
ディレクトリにファイルを書き込んでいます!
これで、クライアント側から送られてきた画像データを保存することができました!
まとめ
ということで今回は、Nuxt を使ってネイティブのカメラアプリを起動したり、画像を選択してその画像を API に送信する機能を実装してみました!
何かが皆さんのお役に立てば幸いです!
Discussion