Laravel + vue.jsでシンプルファイルアップロード
本文書はLaravel9に対応しています。
本文書ではLaravelとVue.jsを利用したファイルアップロードの手順を解説しています。アップロードするファイルのチェックやバリデーションなどの設定は何も行っていないためすぐに本番環境で利用できるものではありません。しかしLaravel+Vue.js環境でファイルをアップロードする際の基本となる部分なのでLaravel+Vue.jsでファイルのアップロードの実装を考えている人はぜひこの機会に理解を深めてください。
本文書ではLaravel5.8 + Vue 2とLaravel9 + Vue3の環境で動作確認を行っています。Vue 2ではOptions APIを利用してVue3ではComposition APIを利用して設定しています。Laravel9ではデフォルトではVueを利用することができないのでnpm installコマンドを利用してVueをインストールして設定を行っています。
環境の構築
Laravel, vue.jsのインストールを行い、データベース、アップロードファイルを保存するためのテーブルの作成を行います。Laravel9とLaravel5.8の2つのバージョンで動作確認を行っています。環境構築についての手順が必要でない場合はこの章はスキップしてください。
Laravel9の場合
Laravelのインストール
composerを利用してLaravelのインストールを行います。インストールが完了するとlaravel9フォルダが作成されるのでlaravel9フォルダに移動します。
$ composer create-project --prefer-dist laravel/laravel laravel9
laravel9フォルダへの移動後にLaravelの開発用サーバを起動してブラウザでhttp://127.0.0.1:8000にアクセスしてください。
% php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Wed Feb 9 11:05:44 2022] PHP 8.0.7 Development Server (http://127.0.0.1:8000) started
下記の画面が表示されることを確認します。
Vue 3のインストールと設定
Laravel9でVue3を利用するためにはnpmコマンドでインストールを行う必要があります。vueをインストールするとVue 3がインストールされます。
% npm install vue
インストールが完了したらresources¥js¥app.jsファイルを開いてVueの設定を行います。#appにコンポーネントAppをマウントしています。
require('./bootstrap');
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vueファイルをjsフォルダの中に作成します。
<template>
<h1>Hello World</h1>
</template>
次にwelcome.blade.phpファイルを以下のように更新します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Laravel9 Vue FileUpload</title>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
<div id="app">
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
LaravelはLaravel mixを介してWebpackを利用してビルドを行っているため設定ファイルであるwebpack.mix.jsファイルにvue()を追加する必要があります。
mix.js('resources/js/app.js', 'public/js')
.vue()
.postCss('resources/css/app.css', 'public/css', [
//
]);
webpack.mix.jsファイルを更新したらnpm run devコマンドでビルドを行います。実行するとVue-loaderに関するメッセージが表示されます。
% npm run dev
> dev
> npm run development
> development
> mix
Additional dependencies must be installed. This will only take a moment.
Running: npm install vue-loader@^16.2.0 --save-dev --legacy-peer-deps
Finished. Please run Mix again.
npmコマンドでvue-loaderのインストールを行います。
% npm install vue-loader@^16.2.0 --save-dev
インストール完了後、再度npm run devコマンドを実行してください。
% npm run dev
//略
webpack compiled successfully
ビルドが完了したらhttp://127.0.0.1:8000/にアクセスを行ってください。App.vueファイルのtemplateタグに記述したHello Worldが表示されたらVueの設定は問題なく完了しています。
App.vueファイルにファイルをアップロードするためにinput要素を追加します。
<template>
<div class="content">
<h1>File Upload</h1>
<p><input type="file"></p>
<button>アップロード</button>
</div>
</template>
<style>
.content{
margin:5em;
}
</style>
更新が完了したらnpm run watchを実行します。npm run watchコマンドを実行することでファイルの監視が行われ更新を検知するとビルダが行われます。
$ npm run watch
ブラウザでアクセスし、下記のファイルアップロード画面が表示されたらアップロードしたファイルの情報を保存するテーブルの作成を行います。
データベースの設定
アップロードしたファイルのパスを保存するためにデータベースを作成し、テーブルの作成を行います。データベースはsqliteを使用します。touchコマンドでsqliteデータベース用のファイルを作成しています。この時点でファイルの中身は空です。
$ touch database/database.sqlite
.envファイルでデータベースの接続に関する設定をmysqlからsqliteに変更します。
変更前はmysqlの設定になっています。
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
変更後は下記のようになります。DB_CONNECTION以外は削除します。
DB_CONNECTION=sqlite
テーブルはLaravelがデフォルトで準備しているusersテーブルを使用します。usersのマイグレーションファイルにファイルのパスを保存する列(file_path)を追加します。
マイグレーションファイルはdatabase/migrationsの2014_10_12_000000_create_users_table.phpです。passwordの列の下にfile_pathを追加します。
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('file_path');
$table->rememberToken();
$table->timestamps();
});
}
php artisan migrateコマンドでテーブルを作成してください。
$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (3.34ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (3.68ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (1.77ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (2.31ms)
tinkerを使用して、ユーザの作成を行います。
% php artisan tinker
Psy Shell v0.11.1 (PHP 8.0.7 — cli) by Justin Hileman
>>> $user = new User
[!] Aliasing 'User' to 'App\Models\User' for this Tinker session.
=> App\Models\User {#3504}
>>> $user->name = 'john'
=> "john"
>>> $user->email = 'john@example.com'
=> "john@example.com"
>>> $user->password = bcrypt('password')
=> "$2y$10$YWeCT47MVY5pTwvJNFMgz.sHpufr03dFO9G8jq/1/nNJY8V51neIO"
>>> $user->save()
=> true
ここまでの処理でファイルのアップロードを行うための環境の構築は完了です。
Laravel5.8の場合
Laravelのインストール
composerを利用してLaravelのインストールを行います。インストールが完了するとlaravel5.8フォルダが作成されるのでlaravel5.8フォルダに移動します。
$ composer create-project --prefer-dist laravel/laravel laravel5.8
laravel5.8フォルダへの移動後にLaravelの開発用サーバを起動してブラウザでhttp://127.0.0.1:8000にアクセスしてください。
$ php artisan serve
Laravel development server started: <http://127.0.0.1:8000>
下記の画面が表示されることを確認します。
vue.jsのインストール
Laravelのインストールが完了したら、npmを利用してvue.jsを含めたJavaScriptのライブラリのインストールを行います。Laravelのインストールディレクトリで実行してください。
$ cd laravel5.8/
$ npm install
JavaScriptのライブラリのインストールが完了したら、npm run devコマンドを実行してビルドが正常に動作するか一度確認します。
$ npm run dev
vue.jsのコンポーネントファイルExampleComponent.vueの内容がブラウザに表示されるようにresources¥views¥welcome.blade.phpの書き換えを行います
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Laravel Vue FileUpload</title>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
<div id="app">
<example-component></example-component>
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
resources¥js¥components¥ExapleComponent.vueも書き換えを行い、ファイルをアップロードするためのinput要素とbutton要素を追加します。
<template>
<div class="content">
<h1>File Upload</h1>
<p><input type="file"></p>
<button>アップロード</button>
</div>
</template>
<script>
export default {
}
</script>
<style>
.content{
margin:5em;
}
</style>
書き換えが終わったら、npm run watchを実行します。
$ npm run watch
ブラウザでアクセスし、下記のファイルアップロード画面が表示されたらアップロードしたファイルの情報を保存するテーブルの作成を行います。
データベースの設定
アップロードしたファイルのパスを保存するためにデータベースを作成し、テーブルの作成を行います。データベースはsqliteを使用します。touchコマンドでsqliteデータベース用のファイルを作成しています。この時点でファイルの中身は空です。
$ touch database/database.sqlite
.envファイルでデータベースの接続に関する設定をmysqlからsqliteに変更します。
変更前はmysqlの設定になっています。
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
変更後は下記のようになります。DB_CONNECTION以外は削除します。
DB_CONNECTION=sqlite
テーブルはLaravelがデフォルトで準備しているusersテーブルを使用します。usersのマイグレーションファイルにファイルのパスを保存する列(file_path)を追加します。
マイグレーションファイルはdatabase/migrationsの2014_10_12_000000_create_users_table.phpです。passwordの列の下にfile_pathを追加します。
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('file_path');
$table->rememberToken();
$table->timestamps();
});
}
php artisan migrateコマンドでテーブルを作成してください。
い $ php artisan migrate
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table
tinkerを使用して、ユーザの作成を行います。
$ php artisan tiker
>>> $user = new User
=> App\User {#2969}
>>> $user->name = 'john'
=> "john"
>>> $user->email = 'john@example.com'
=> "john@example.com"
>>> $user->password = bcrypt('password')
=> "$2y$10$ugq8xVC6CX.xjlztHvB8buN.3XvgJYPIHlxAOiqtp2a.P9lhQq1vC"
>>> $user->file_path = ''
=> ""
>>> $user->save()
=> true
ここまでの処理で環境の構築は完了です。
ファイルアップロードの設定
ファイルをアップロードするためのLaravelとVueの環境が整ったので、アップロードする手順に進みます。Laravel9とLaravel5.8で別々に説明を行っています。Vue2のOptions APIを利用する場合はLaravel5.8の手順、Vue3のComposition APIを利用する場合はLaravel9の手順を参考にしてください。
Laravel9の場合
ユーザが選択したファイル情報の取得
ユーザがファイル選択を行った時にvue.js側で選択したファイル情報の取得を行います。
input要素にv-onディレクティブでchangeイベントを設定してファイルが選択されたかどうかを監視します。ファイル選択が行われるとfileSelectedメソッドが実行されます。
<template>
<div class="content">
<h1>File Upload</h1>
<p><input type="file" @change="fileSelected" /></p>
<button>アップロード</button>
</div>
</template>
scriptタグにsetupを追加しfileSelected関数を設定します。fileSelected関数では引数で受け取ったevent変数を利用して選択したファイル情報を取得します。
<script setup>
const fileSelected = (event) => {
console.log(event);
};
</script>
ファイルを選択し、デベロッパーツールのコンソールで確認するとファイルを選択することで実行されたfileSelected関数によってeventの中身が表示されます。
表示されているeventの内容を確認していくとtargetプロパティの中のfilesに選択したファイルの情報が入っていることが確認できます。
このファイル情報を保存するためにref関数を使ってfileInfoを定義して取得したファイルの情報をfileInfoの中に保存します。
<script setup>
import { ref } from "vue";
const fileInfo = ref("");
const fileSelected = (event) => {
fileInfo.value = event.target.files[0];
};
</script>
ファイルのアップロード設定
ファイル情報の取得の設定が完了したのでアップロードボタンをクリックするとファイルアップロードが開始される設定を追加します。
buttonにv-onディテクティブでclickイベントを設定しメソッドの名前はfileUploadとします。ボタンをクリックするとfileUploadメソッドが実行されます。
<div class="content">
<h1>File Upload</h1>
<p><input type="file" @change="fileSelected" /></p>
<button @click="fileUpload">アップロード</button>
</div>
fileUploadメソッドの中でformDataオブジェクトとaxiosを利用してファイルのアップロードを行います。
ファイルをアップロード先/api/fileuploadのルーティング設定をLaravel側で行っていないので、ファイルを選択しアップロードボタンを押すと404NotFoundエラーが発生します。
Laravelでのルーティング設定
/api/fileuploadのルーティング設定をLaravel側で行います。api.phpを開いて/api/fileuploadの追加を行います。ファイルの情報が送信されてきているか確認するためにdd関数を使って送信されている内容を確認します
Route::post('fileupload',function(){
dd(request()->all());
});
デベロッパーツールのコンソールのNetworkタグを使うとファイルがLaravel側に送信されていること確認できます。
ファイルの保存
Laravel側にファイルが送信されていることが確認できたのでファイルの保存を行います。ファイル名はgetClientOriginalName()メソッドで取得することができます。storageAsメソッドを使うとstorage/app/publicの下にファイルが保存されます。
Route::post('fileupload',function(){
$file_name = request()->file->getClientOriginalName();
request()->file->storeAs('public/',$file_name);
});
ファイルのアップロードを行いstorage/app/publicフォルダを確認すると選択したvue.jpgファイルが保存されていることが確認できます。
// laravel9/storage/app/public
% ls
vue.jpg
保存された場所はブラウザを通してアクセスできない場所のため公開ディレクトリのpublicにシンボリックリンクを貼る必要があります。php artisan storage:linkコマンドを実行するとシンボリックリンクが貼られます。
% php artisan storage:link
The [/Users/mac/Desktop/laravel9/public/storage] link has been connected to [/Users/mac/Desktop/laravel9/storage/app/public].
The links have been created.
シンボリックリンクが貼られたので、ブラウザでhttp://127.0.0.1:8000/storage/vue.jpgにアクセスするとアップロードされたファイルを確認することができます。
ここまでの設定でVueからファイルをアップロードしてアップロードしたファイルをLaravel側で保存できるようになりました。
テーブルへのパスの保存
アップロードしたファイルの保存場所を保存するためにusersテーブルのfile_path列にパス情報の登録を行います。
usersテーブルのfile_path列に保存を行うのでどのユーザのfile_pathに保存するのか保存するユーザを識別する必要があります。通常であればログインしたユーザのIDを利用することができますが本文書ではファイルアップロードを目的にしているのでユーザID1をもつユーザのfile_pathにアップロードしたファイルのパス(/storage/ファイル名)を保存します。
作成したユーザの情報をデータベースの管理ソフトやtinkerなどを利用して確認してIDの設定を行ってください。本文書ではtinkerを利用してユーザの作成を行ったのでIDは1ですがユーザを再作成した場合などはIDは1ではないので注意してください。
追加したfile_path列に書き込みを許可するためにモデルファイルUser.phpのfile_pathを$fillable変数に追加する必要があります。
protected $fillable = [
'name', 'email', 'password','file_path'
];
ユーザID1のfile_pathにパスの更新を行うためにルーティングファイルapi.phpを以下のように書き換えます。更新が完了すると$user情報を戻します。
Route::post('fileupload',function(){
$file_name = request()->file->getClientOriginalName();
request()->file->storeAs('public/',$file_name);
$user = App\Models\User::find(1);
$user->update(['file_path' => '/storage/'.$file_name]);
return $user;
});
ファイルの送信を行い、コンソールで$userの情報が戻ってきており、file_pathに送信したファイルの情報が入っているか確認します。下記ではfile_pathに/storage/vue.pngを確認することができます。
file_pathにファイルのパスの情報が入っていたら、サーバへのファイルの保存とデータベースへのファイルのパス情報の保存は完了です。
ここからは戻り値$userを使ってvue.js側でアップロードしたファイルを表示させる設定を行います。
vue.js側の処理
戻り値の$userを使用して、アップロードしたファイルの画像をブラウザに表示させる処理を追加します。
新規にref関数を利用してuserとshowUserImage変数を定義します。user変数には戻ってきたuser情報を保存しshowUserImageはv-ifディレクティブで画像の表示・非表示を切り替えるために使用します。
const fileInfo = ref("");
const user = ref(null);
const showUserImage = ref(false);,
axiosでファイルの送信を行い、戻ってきた値をuserデータに入力します。userデータのfile_pathに値が設定されていたら、showUserImageの値をtrueに変更します。
axios.post("/api/fileupload", formData).then((response) => {
user.value = response.data;
if (response.data.file_path) showUserImage.value = true;
});
template側ではv-ifディレクティブを使用して、画像の表示・非表示を切り替えます。
<template>
<div class="content">
<h1>File Upload</h1>
<p><input type="file" @change="fileSelected" /></p>
<button @click="fileUpload">アップロード</button>
<div v-if="showUserImage">
<img :src="user.file_path" />
</div>
</div>
</template>
アップロードが完了するとアップロードした画像が表示されます。
Laravel5.8の場合
ユーザが選択したファイル情報の取得
ユーザがファイル選択を行った時にvue.js側で選択したファイル情報の取得を行います。
input要素にv-onディレクティブのchangeイベントを設定してファイルが選択されたかどうかを監視します。ファイル選択が行われるとfileSelectedメソッドが実行されます。
<p><input type="file" v-on:change="fileSelected"></p>
scriptタグにメソッドfileSelectedを追加し、event変数を利用して選択したファイル情報を取得します。
export default {
methods:{
fileSelected(event){
console.log(event)
}
}
}
ファイルを選択し、デベロッパーツールのコンソールで確認するとeventから選択されたファイルの情報を取得することができます。
Eventオブジェクトのtargetプロパティの中のfilesに選択したファイルの情報が入っています。
このファイル情報を保存するためにdataにfileInfoを追加して取得したファイルの情報をfileInfoの中に保存します。
export default {
data: function(){
return {
fileInfo: ''
}
},
methods:{
fileSelected(event){
this.fileInfo = event.target.files[0]
}
}
}
ファイルのアップロード設定
ファイル情報の取得の設定が完了したので、アップロードボタンをクリックするとファイルアップロードが開始される設定を追加します。
buttonにv-onディレクティブのclickイベントを設定し、メソッドの名前はfileUploadとします。ボタンをクリックするとfileUploadメソッドが実行されます。
<p><input type="file" v-on:change="fileSelected"></p>
<button v-on:click="fileUpload">アップロード</button>
fileUploadメソッドの中でformDataオブジェクトとaxiosを利用してファイルのアップロードを行います。
ファイルをアップロード先/api/fileuploadのルーティング設定をLaravel側で行っていないので、ファイルを選択しアップロードボタンを押すと404NotFoundエラーが発生します。
fileUpload(){
const formData = new FormData()
formData.append('file',this.fileInfo)
axios.post('/api/fileupload',formData).then(response =>{
console.log(response)
});
}
Laravelでのルーティング設定
/api/fileuploadのルーティング設定をLaravel側で行います。api.phpを開いて/api/fileuploadの追加を行います。ファイルの情報が送信されてきているか確認するためにdd関数を使って送信されている内容を確認します。
Route::post('fileupload',function(){
dd(request()->all());
});
コンソールのNetworkタグを使うとファイルがLaravel側に送信されていることが確認できます。
ファイルの保存
Laravel側にファイルが送信されていることが確認できたのでファイルの保存を行います。ファイル名はgetClientOriginalName()メソッドで取得することができます。storageAsメソッドを使うとstorage/app/publicの下にファイルが保存されます。
Route::post('fileupload',function(){
$file_name = request()->file->getClientOriginalName();
request()->file->storeAs('public/',$file_name);
});
storage/app/publicディレクトリを確認すると選択したvue.pngファイルが保存されていることが確認できます。
$ ls
vue.png
保存された場所はブラウザを通してアクセスできない場所のため公開ディレクトリのpublicにシンボリックリンクを貼る必要があります。php artisan storage:linkコマンドを実行するとシンボリックリンクが貼られます。
$ php artisan storage:link
The [public/storage] directory has been linked.
シンボリックリンクが貼られたので、ブラウザでhttp://127.0.0.1:8000/storage/vue.pngにアクセスするとアップロードされたファイルを確認することができます。
テーブルへのパスの保存
アップロードしたファイルの保存場所を保存するためにusersテーブルのfile_path列にパス情報の登録を行います。
usersテーブルのfile_path列に保存を行うので、どのユーザのfile_pathに保存するのか保存するユーザを識別する必要があります。今回はファイルアップロードを目的にしているので、ユーザID1をもつユーザのfile_pathにアップロードしたファイルのパス(/storage/ファイル名)を保存します。
作成したユーザの情報をtinkerなどを利用して確認してIDの設定を行ってください。ユーザを再作成した場合などはIDは1ではありません。
追加したfile_path列に書き込みを許可するためにモデルファイルUser.phpのfile_pathを$fillable変数に追加する必要があります。
protected $fillable = [
'name', 'email', 'password','file_path'
];
ユーザID1のfile_pathにパスの更新を行うためにルーティングファイルapi.phpを以下のように書き換えます。更新が完了すると$user情報を戻します。
Route::post('file_upload',function(){
$file_name = request()->file->getClientOriginalName();
request()->file->storeAs('public/',$file_name);
$user = App\User::find(1);
$user->update(['file_path' => '/storage/'.$file_name]);
return $user;
});
ファイルの送信を行い、コンソールで$userの情報が戻ってきており、file_pathに送信したファイルの情報が入っているか確認します。下記ではfile_pathに/storage/vue.pngを確認することができます。
file_pathにファイルのパスの情報が入っていたら、サーバへのファイルの保存とデータベースへのファイルのパス情報の保存は完了です。
ここからは戻り値$userを使ってvue.js側でアップロードしたファイルを表示させる設定を行います。
vue.js側の処理
戻り値の$userを使用して、アップロードしたファイルの画像をブラウザに表示させる処理を追加します。
新規にvue.jsのデータプロパティにuserとshowUserImageのデータを追加します。userには、戻ってきたuser情報、showUserImageはv-ifディレクティブで画像の表示・非表示を切り替えるために使用します。
data: function(){
return {
fileInfo: '',
user: '',
showUserImage: false
}
},
axiosでファイルの送信を行い、戻ってきた値をuserデータに入力します。userデータのfile_pathに値が設定されていたら、showUserImageの値をtrueに変更します。
axios.post('/api/fileupload',formData).then(response =>{
this.user = response.data
if(response.data.file_path) this.showUserImage = true
});
template側ではv-ifディレクティブを使用して、画像の表示・非表示を切り替えます。
<div class="content">
<h1>File Upload</h1>
<p><input type="file" v-on:change="fileSelected"></p>
<button v-on:click="fileUpload">アップロード</button>
<p v-if="showUserImage"><img v-bind:src="user.file_path"></p>
</div>
アップロードが完了するとアップロードした画像が表示されます。
選択したファイルのチェックも行わない簡易的なコードですが、vue.jsをフロントエンドとしてLaravelと連携して、サーバへのファイルのアップロードとusersテーブルへのファイルパスの設定、アップロードしたファイルの表示まで行うことができました。これを元にユーザフレンドリーな実用的なファイルアップロードにチャレンジしてみてください。
今回更新したExampleComponent.vue内のコードの全体は以下となります。
<template>
<div class="content">
<h1>File Upload</h1>
<p><input type="file" v-on:change="fileSelected"></p>
<button v-on:click="fileUpload">アップロード</button>
<p v-show="showUserImage"><img v-bind:src="user.file_path"></p>
</div>
</template>
<script>
export default {
data: function(){
return {
fileInfo: '',
user: '',
showUserImage: false
}
},
methods:{
fileSelected(event){
this.fileInfo = event.target.files[0]
},
fileUpload(){
const formData = new FormData()
formData.append('file',this.fileInfo)
axios.post('/api/fileupload',formData).then(response =>{
this.user = response.data
if(response.data.file_path) this.showUserImage = true
});
}
}
}
</script>
<style>
.content{
margin:5em;
}
</style>