Vue.jsとFlaskでフルスタックなWebアプリの開発環境を構築 その2〜〜投稿画面作成〜〜
はじめに
前回作ったこちらの環境を用いて、タスク管理アプリ的なものを作成していきたいと思います。
タスクのモデルを作成(Flask)
まずはこちらのサイトを参考に、タスクのクラスを作成し一通りFlask側のみで動作するところまでを作成いたします。
2. Flaskチュートリアル
前回からのファイル構成変更点
その前に、前回作成した構成を若干修正します。
前回は下記の構成だったかと思いますが、今後の扱いやすさを考慮しファイル名とその中の記載内容を変更します。
app_dir ┣appserver.py → manage.pyにファイル名変更 ┣frontend/ ┗backend/ ┣api.py ┣application.py → __init__.pyにファイル名変更 ┗config.py
それぞれファイル名を変更したものの中身は以下です。
# __init__.py(←application.py) from flask import Flask from flask_cors import CORS app = Flask('FLASK-VUE', static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') from backend.api import api app.register_blueprint(api, url_prefix="/api") cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): if app.debug: return requests.get('http://localhost:8080/{}'.format(path)).text return render_template("index.html")
# maange.py (←appserver.py) from backend import app if __name__ == '__main__': app.run()
sqliteを使用する設定
チュートリアルに倣い、sqliteはSQLAlchemyで操作します。
まずは以下コマンドでインストールします。
Flask-SQLAlchemyのインストール
$ pip install Flask-SQLAlchemy
次にconfig.py
を編集します.
# config.py import os class BaseConfig(object): DEBUG = True ## 以下を追記 SQLALCHEMY_DATABASE_URI = 'sqlite:///backend.db' # cookieを暗号化する秘密鍵 SECRET_KEY = os.urandom(24)
# __init__py from flask import Flask from flask_cors import CORS import requests from flask_sqlalchemy import SQLAlchemy # ここを追記 app = Flask('FLASK-VUE', static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') db = SQLAlchemy(app) # ここを追記 from backend.api import api app.register_blueprint(api, url_prefix="/api") cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): if app.debug: return requests.get('http://localhost:8080/{}'.format(path)).text return render_template("index.html")
models.pyの作成
チュートリアルに倣い、以下のように記述します。
# backend/models.py from backend import db class Task(db.Model): __tablename__ = 'tasks' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.Text) text = db.Column(db.Text) def to_dict(self): return dict( id=self.id, title=self.title, text=self.text ) def __repr__(self): return '<Task id={id} title={title!r}>'.format( id=self.id, title=self.title) def init(): db.create_all()
コマンドラインからデータベースを初期化します。
$ python >>> from backend.models import init >>> init()
テータを登録してみます。
$ python >>> from backend.models import Task, db >>> Task.query.all() [] >>> task = Task(title='title', text='text') >>> db.session.add(task) >>> db.session.commit() >>> Task.query.all() [<Task id=1 title=u'title'>] >>> task = Task.query.get(1) >>> task <Task id=1 title=u'title'> >>> task.title = 'hello' >>> task.text = 'Hello world' >>> db.session.add(task) >>> db.session.commit() >>> Task.query.all() [<Task id=1 title=u'hello'>]
APIの作成(Flask)
まずはFlask側で登録のAPIを作成します。
基本は先程のチュートリアルを参考にしつつ、以下のように記述します。
# backend/api.py from flask import Blueprint, jsonify, request, url_for, make_response from random import * from flask_cors import CORS from backend import app, db from backend.models import Task api = Blueprint('api', __name__) @api.route('/hello/<string:name>/') def say_hello(name): response = { 'msg': "Hello {}".format(name) } return jsonify(response) @api.route('/random') def random_number(): response = { 'randomNumber': randint(1, 100) } return jsonify(response) @api.route('/get', methods=['GET']) def get_taks(): taks = Task.query.order_by(Task.id.desc()).all() taks_dict = [task.to_dict() for task in taks] return jsonify(taks_dict) @api.route('/add', methods=['POST']) def add_task(): task = Task( title=request.form['title'], text=request.form['text'] ) db.session.add(task) db.session.commit() task = Task.query.order_by(Task.id.desc()).first() id = str(task.id) r = make_response(id) return r @api.route('/delete', methods=['POST']) def delete_task(): id=request.form['id'] task = Task.query.get(id) db.session.delete(task) db.session.commit() r = make_response(id) return r
http://127.0.0.1:5000/api/get
にアクセスして、以下のようにレスポンスが返却されればOKです。
[ { "id": 1, "text": "Hello world", "title": "hello" } ]
登録画面の作成(Vue.js)
ルーティングの追加
続いて、Vue.jsのSPA側で、タスクの登録と閲覧をする画面を作成します。
$ cd frontend/src/component $ cp Home.vue Tasks.vue
また、SPA側のルーティングにもタスク画面を表示するように記述します。
// frontend/src/router/index.js /////// 省略 /////// const routerOptions = [ { path: '/', component: 'Home' }, { path: '/about', component: 'About' }, { path: '/tasks', component: 'Tasks' },//ここを追記 { path: '*', component: 'NotFound' } ] /////// 省略 ///////
bootstrap-vueの導入
登録画面を作成するにあたってはbootstrap-vueを使用したいと思います。
公式サイトに記載されている通り、以下コマンドでインストールします。
$ npm install jquery $ npm install bootstrap-vue
また、こちらも記載されている通り、frontend/src/main.js
とfrontend/src/App.vue
に以下の記述を記載します。
// frontend/src/main.js import Vue from 'vue' import BootstrapVue from 'bootstrap-vue' //ここを追記 import App from './App' import router from './router' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, render: h => h(App) }) Vue.use(BootstrapVue) //ここを追記
// frontend/src/App.vue /////// 省略 /////// <script> import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' export default { name: 'App' } </script> /////// 省略 ///////
画面の作成
そして、Tasks.vue
にて先程作成したバックエンド側のAPIを叩き、UIに表示するviewを作成します。
デザインは適当です笑。
// frontend/src/component/Task.vue <template> <div class="container"> <div align="center"> <div class="col-sm-8 col-md-6 col-lg-6"> <p>タスクの登録</p> <b-form v-if="show"> <b-form-group label="Title:" label-for="title"> <b-form-input id="title" type="text" required placeholder="Enter title"> </b-form-input> </b-form-group> <b-form-group label="Text:" label-for="title"> <b-form-textarea id="text" placeholder="Enter something" :rows="3" :max-rows="6"> </b-form-textarea> </b-form-group> <div align="center"> <div class="col-sm-4 col-md-2 col-lg-2"> <b-button block @click="addTask" variant="success">Add</b-button> </div> </div> </b-form> </div> </div> <b-list-group v-for="(task, index) in tasks" :key='index'> <b-list-group-item> {{ task.title }}<br> {{ task.text }} <b-button v-bind:src="task.id" block @click="deleteTasks(index, task.id)">削除</b-button> </b-list-group-item> </b-list-group> </div> </template> <script> import axios from 'axios' export default { data () { return { randomNumber: 0, tasks: [], show: true } }, methods: { getTasks () { const path = 'http://localhost:5000/api/get' axios.get(path) .then(response => { this.tasks = response.data }) .catch(error => { console.log(error) }) }, addTask () { const path = 'http://localhost:5000/api/add' var title = document.getElementById('title') var text = document.getElementById('text') let params = new URLSearchParams() params.append('title', title.value) params.append('text', text.value) var titleValue = title.value var textValue = text.value title.value = '' text.value = '' axios.post(path, params) .then(response => { var id = response.data var task = {'id': id, 'text': textValue, 'title': titleValue} this.tasks.unshift(task) console.log(response) }) .catch(error => { console.log(error) }) }, deleteTasks (taskIndex, taskId) { console.log(taskIndex) console.log(taskId) const path = 'http://localhost:5000/api/delete' let params = new URLSearchParams() params.append('id', taskId) this.tasks.splice(taskIndex, 1) axios.post(path, params) .then(response => { console.log(response) }) .catch(error => { console.log(error) }) } }, created () { this.getTasks() } } </script>
http://127.0.0.1:5000/tasks
を開けば以下のように登録と削除ができる画面が動作すると思います。
あまりタスク管理っぽくないですが。笑
終わりに
これで、バックエンドとフロントエンドの役割が明確なWebアプリを作成出来ました。
あとはバックエンドのFlaskでAPIを作成し、フロントエンドのVue.jsでUIを描画すればいろんなWebアプリが作れるようになります。
次回はこのWebアプリにログイン機能をつけたいと思います。
参考サイト
Vue.jsとFlaskでフルスタックなWebアプリの開発環境を構築 その1〜〜環境構築〜〜
- はじめに
- vue-cliで雛形のインストール(フロントエンド)
- サンプルページの追加
- ビルドディレクトリの変更
- Flaskのインストール(バックエンド)
- Flaskのサンプル作成
- 404ページの追加
- APIの追加
- 終わりに
はじめに
最近のWebアプリはReact.jsやVue.jsを使用したSPAが主流かと思いますが、データ取得のAPIを実装したりなど、サーバサイドの実装も必要になります。
自分はJavascriptに不慣れなのでサーバサイドはJavascriptではなくPythonを使いたかったので、Flaskと共存できる構成にしたかったのがきっかけです。
いろいろと探してみたらすでに先人がやっていました。
Full-stack single page application with Vue.js and Flask
Single Page Apps with Vue.js and Flask
以下のような構成で開発できるようにします。
app_dir ┗frontend // Vue.jsのプログラム ┗backend // Flaskのプログラム
英語記事なのと、それぞれの記事で若干やっていることが異なるため
それぞれを混ぜる形で環境を構築したので、備忘として自分の言葉でまとめておこうと思います。
vue-cliで雛形のインストール(フロントエンド)
以下コマンドでvue-cliをインストールできます。
$ npm install -g vue-cli
アプリの雛形をインストールします。
$ vue init webpack frontend ? Project name (frontend):[Enter] ? Project description (A Vue.js project):[Enter] ? Author (hogehoge <hogehogehogehoge@gmail.com>):[Enter] ? Vue build (Use arrow keys):[Enter] ❯ Runtime + Compiler: recommended for most users Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere ? Install vue-router? (Y/n):[Enter] ? Use ESLint to lint your code? (Y/n):[Enter] ? Pick an ESLint preset (Use arrow keys) ❯ Standard (https://github.com/standard/standard) Airbnb (https://github.com/airbnb/javascript) none (configure it yourself) ? Set up unit tests (Y/n):[Enter] ? Pick a test runner (Use arrow keys):[Enter] ❯ Jest Karma and Mocha none (configure it yourself) ? Setup e2e tests with Nightwatch? (Y/n):[Enter] ? Should we run `npm install` for you after the project has been created? (recommended) (Use arrow keys) ❯ Yes, use NPM Yes, use Yarn No, I will handle that myself vue-cli · Generated "frontend". # Installing project dependencies ... # ======================== (略) # Project initialization finished! # ======================== To get started: cd frontend npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack $ cd frontend $ npm install # after installation $ npm run dev DONE Compiled successfully in 21497ms 12:42:34 I Your application is running here: http://localhost:8080
ブラウザでhttp://localhost:8080
を開いて以下の画面が表示されれば、アプリの雛形インストール完了です。
サンプルページの追加
Vue.jsの構成や説明はここでは記載しませんが、練習用にページを追加してみましょう。
frontend/src/components
にHome.vue
とAbout.vue
を追加します。
// Home.vue <template> <div> <p>Home page</p> </div> </template>
// About.vue <template> <div> <p>About</p> </div> </template>
また、frontend/src/router/index.js
を以下のように編集します。
import Vue from 'vue' import Router from 'vue-router' // もともと記載されていたものは削除orコメントアウト // import HelloWorld from '@/components/HelloWorld' // Vue.use(Router) // export default new Router({ // routes: [ // { // path: '/', // name: 'HelloWorld', // component: HelloWorld // } // ] // }) const routerOptions = [ { path: '/', component: 'Home' }, { path: '/about', component: 'About' } ] const routes = routerOptions.map(route => { return { ...route, component: () => import(`@/components/${route.component}.vue`) } }) Vue.use(Router) export default new Router({ routes, mode: 'history' })
このように編集したら再度ブラウザでlocalhost:8080
とlocalhost:8080/about
を開いてページが表示されたらOKです。
ビルドディレクトリの変更
npm run build
コマンドで立ち上がるのは開発用のサーバなので、実際にサービス提供する際はブラウザが解釈できる形にビルドする必要があります。
そのビルドした結果がどこに出力されるかはfrontend/config/index.js
に記載があります。
もともとの記載は、
build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist'), //////以下略////// }
となっており、frontend/dist
配下にビルドされたソースコードが出力される設定となっております。
このままだとFlaskでindex.htmlにアクセスするときにfrontend
配下を参照しなくてはならなくなってしまうので、管理上明確に分離するためにfrontend
の上の階層に出力されるようにします。
build: { // Template for index.html index: path.resolve(__dirname, '../../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../../dist'), //////以下略////// }
以下コマンドでビルドします。
$ npm run build
以下のような構成になります。
app_dir ┗frontend // Vue.jsのプログラム ┗dist // コンパイルされたVue.jsのプログラム
$ ls ../dist/
index.html static
Flaskのインストール(バックエンド)
バックエンドのプログラムには今回はFlaskを使用します。
元記事にはPythonの環境を整えるのにvirtualenvを使用していますが、僕はpyenv(pyenv-virtualenv)のほうが使い慣れているのでこちらを使います。
pyenvは以下コマンドでインストールできます。
$ brew install pyenv $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile
以下コマンドで本アプリ用のPython3.6.1の環境を作ります。
$ pyenv install --list $ pyenv install 3.6.1 $ pyenv virtualenv 3.6.1 vue-flask $ pyenv versionos system 3.6.1 3.6.1/envs/vue-flask vue-flask
app_dir
に行き、以下のコマンドを実行します。
$ pyenv local vue-flask (vue-flask) $ # このようにプロンプトが変わったらOK
これで、app_dir
内に閉じたPython3.6.1の環境が作れました。
(この中でパッケージのインストールを行っても他の環境には影響ありません。)
以下コマンドでFlaskをインストールします。
$ pip install Flask
$ mkdir backend
Flaskのサンプル作成
いよいよFlaskでバックエンドのプログラムを作成していきます。
今回は以下のようなファイル構成にしようと思います。
app_dir ┗appserver.py ┗frontend/ ┗backend/ ┗api.py ┗application.py ┗config.py ┗router.py
それぞれのファイルの中身は以下です。
# appserver.py from backend.application import create_app from flask import render_template app = create_app() @app.route('/') def index(): return render_template("index.html") if __name__ == '__main__': app.run()
# application.py from flask import Flask def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') return app
# config.py class BaseConfig(object): DEBUG = True
ポイントは、application.py
において、
app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist")
を指定することです。
これを指定することで、Flaskのテンプレート参照先が./dist
配下になり、Vue.jsでビルドしたindex.html
が参照されることになります。
以下コマンドで、Flaskの開発用サーバが立ち上がります。
$ python appserver.py * Serving Flask app "FLASK-VUE" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 977-628-540
http://localhost:5000/
にアクセスすると、先程vue.jsで作成したホーム画面が表示されます。
しかし、vue.js側でルーティングを実装したはずの、http://localhost:5000/about
にアクセスしても、Not Foundとなってしまいます。
これは、Flask側ではルーティングをしていないにもかかわらず、Flask側で処理しようとしてしまっているためです。
そこで以下をappserver.py
に追記して、/
以下のパスをvue.jsでビルドしたindex.html
にリダイレクトするようにします。
# appserver.py from backend.application import create_app from flask import render_template app = create_app() #### 以下を追記 ### @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): return render_template("index.html") ################# if __name__ == '__main__': app.run()
再度、http://localhost:5000/about
にアクセスするとAboutページが表示されます。
404ページの追加
これで、ブラウザ上のルーティングがvue.jsでビルドしたindex.html
にリダイレクトされるので、vue.js側で404ページも追加しておかなくてはなりません。
frontend/src/components
配下に以下NotFound.vue
を作成します。
// NotFound.vue <template> <div> <p>404 - Not Found</p> </div> </template>
また、frontend/src/router/index.js
に以下の通り、ページがない場合のパスを追記します。
const routerOptions = [ { path: '/', component: 'Home' }, { path: '/about', component: 'About' }, { path: '*', component: 'NotFound' } ]
http://localhost:8080/
配下の適当なパスにアクセスしてNotFoundのページが表示されればOKです。
npm run build
でビルドすればFlask側のサーバhttp://localhost:5000/
でも同様にNotFoundのページが表示されます。
APIの追加
さて、基本的な環境は整ってきたので、バックエンド側にAPIエンドポイントを追加していきたいと思います。
バックエンド(Flask)→APIエンドポイントの追加
フロントエンド(Vue.js)→APIアクセスし結果を描画
まずはFlask側にAPIに対するルーティングを追加します。
index.html
に対するルーティングと同様に、そのままappserver.py
に記載しても良いのですが、APIのルーティングは別ファイルで管理したいので、FlaskのBluprintを使用します。
backend
配下にapi.py
というファイルを作成します。
from flask import Blueprint, jsonify, request from random import * api = Blueprint('api', __name__) @api.route('/hello/<string:name>/') def say_hello(name): response = { 'msg': "Hello {}".format(name) } return jsonify(response) @api.route('/random') def random_number(): response = { 'randomNumber': randint(1, 100) } return jsonify(response)
今回は試しに名前に対してHelloのメッセージを返すAPIと乱数を返すAPIを作成しました。
backend/application.py
にてこのAPIのルーティングを記載したBlueprintファイルをimportします。
# application.py from flask import Flask def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') ##### ここを追記 ##### from backend.api import api app.register_blueprint(api, url_prefix="/api") #################### return app
url_prefix="/api"
を指定しているので、ここで記載するルーティングはすべて/api/
配下のルーティングとなります。
http://localhost:5000/api/random
にアクセスすると以下の結果
{ "randomNumber": 38 }
http://localhost:5000/api/hello/hoge
にアクセスすると以下の結果
{ "msg": "Hello hoge" }
上記のような結果になればOKです。
次にfrontend
配下のVue.jsのプログラム側でこのAPIにリクエストをし、画面表示させたいと思います。
まずは、Vue.jsでajaxを利用するために、axiosをインストールします。
$ cd frontend $ npm install --save axios
frontend/src/components/Home.vue
を以下のように書き換えます。
// Home.vue <template> <div> <p>Home page</p> <p>Random number from backend: {{ randomNumber }}</p> <button @click="getRandom">New random number</button> </div> </template> <script> import axios from 'axios' export default { data () { return { randomNumber: 0 } }, methods: { getRandom () { const path = 'http://localhost:5000/api/random' axios.get(path) .then(response => { this.randomNumber = response.data.randomNumber }) .catch(error => { console.log(error) }) } }, created () { this.getRandom() } } </script>
バックエンドのFlask側では、デフォルトでは他のサーバからのリクエストを受け付ける設定になっていないため、フロントエンドのVue.jsからのAjaxのリクエストを受け入れるようにします。
まずは以下コマンドでCORSをインストールします。
$ pip install -U flask-cors
backend/application.py
を以下のように書き換えます。
# application.py from flask import Flask from flask_cors import CORS # ここを追記 def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') from backend.api import api app.register_blueprint(api, url_prefix="/api") # ここを追記 cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) return app
これでhttp://localhost:8080/
アクセスしたときにAPIから取得した乱数を表示することができるようになったのですが、
もし静的なファイルをFlask経由で取得する必要が無いのであれば、COREの機能を使う必要はありません。
その場合、backend/appserver/py
を次のように書き換えればOKです。
# appserver.py from backend.application import create_app from flask import render_template import requests # ここを追記 app = create_app() @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): #### 以下を追記 ### if app.debug: return requests.get('http://localhost:8080/{}'.format(path)).text return render_template("index.html") ################# if __name__ == '__main__': app.run()
必要に応じて以下コマンドを実行。
$ pip install requests
これで、開発モード(DEBUG=1
)の時は、http://localhost:5000
にアクセスした時はhttp://localhost:8080
にリダイレクトされるようになり、Vue.js側の画面でちゃんと乱数が取得できるようになります。
Flask側のサーバhttp://localhost:5000
とVue.js側のサーバhttp://localhost:8080
どちらにアクセスしても同様の結果が得られます。
終わりに
これで、バックエンドのAPIエンドポイントはFlaskで実装し、フロントエンドのUIはVue.jsで実装するフルスタックなアプリケーションが構築できました。
Flask側の開発用サーバとVue.jsの開発用サーバどちらも起動しておけば、それぞれ配下のプログラムを編集した際にホットリロードでリアルタイムに反映されるので、とても開発がしやすいです。
FlaskとVue.jsの詳細な使い方は今後もっと勉強していきたいと思います。
dropbox-sdk-jsを使ってフォルダ内アイテムの共有リンクを取得
はじめに
最近フロントエンドの勉強をしています。
今作っているWebアプリで、Dropboxに保存してある写真を表示するという機能を作りたかったのですが、サーバサイドでAPIを叩き個別に取得して表示させるととても画面レスポンスが遅くなってしまいました。
そこでJavascriptで非同期に取得し、順次画面表示させてユーザーの体感速度を早くしようとしたのですが、そこで少しハマってしまったので解決方法を備忘として残しておきます。
dropbox-sdk-jsの導入
まずはdropbox-sdk-jsを導入します。
今回はnpmではなくCDN版のものを使用します。
Getting started | Dropbox JavaScript SDK
以下をhtmlに記載します。
<script src="https://unpkg.com/dropbox/dist/Dropbox-sdk.min.js"></script> <script src="https://unpkg.com/dropbox/dist/DropboxTeam-sdk.min.js"></script>
フォルダ内アイテム一覧を取得
まずは共有リンクを取得したい画像アイテムのリストを取得します。
filesListFolder
メソッドにフォルダのパスを渡せば取得出来るみたいです。
var dropbox_api_token = "((dropbox_api_token))"; var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token }); var dir_path = "path/to/items" dbx.filesListFolder({path: dir_path}) .then(function(response) { console.log(response.entries) }) .catch(function(error) { console.error(error); });
これはチュートリアルに載っているコードそのままで取得できます。
これでresponse.entries
にアイテム一覧が配列で取得できました。
forループでフォルダ内のアイテムそれぞれの共有リンクを取得
一覧が取得できたので、それぞれのアイテムの共有リンクを取得しようと思います。
本来なら、共有リンクを作成dbx.sharingCreateSharedLinkWithSettings
の後に共有リンクを取得dbx.sharingListSharedLinks
する流れですが、自分の環境だとサーバサイドで共有リンクの作成は完了しているので、リンク取得のみを記述します。
こんな感じです。
var dropbox_api_token = "((dropbox_api_token))"; var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token }); var entries; var urls = [] //共有リンクの配列 var dir_path = "path/to/items" dbx.filesListFolder({path: dir_path}) .then(function(response) { console.log(response.entries) entries = response.entries; for(var i = 0;i<entries.length;i++) { dbx.sharingListSharedLinks({path: entries[i].path_display}) .then(function(response) { urls.push(response.links[0].url); }) } }) .catch(function(error) { console.error(error); }); /////////////////////////////////////// //以下に共有リンクの配列(urls)を取得した後の処理を記載 ///////////////////////////////////////
JavaScript初心者だった自分は、なんとなくこれで動作しそうだなーと思って、こんなコードを書いたのですが、これが全然うまくいきませんでした。
共有リンクが取得できている前提で後の処理を書いても「urlsの中身が空だよー」とエラーになってしまいます。
再帰的に関数を呼び出す
非同期処理が入っているため順番がめちゃくちゃになってしまうようなので、きっちりと順番順番に処理をしてもらうために、以下のように関数を再帰的に呼び出して処理することにしました。
var dropbox_api_token = "((dropbox_api_token))"; var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token }); var urls = [] //共有リンクの配列 var dir_path = "path/to/items" dbx.filesListFolder({path: path}) .then(function(response) { // 関数の呼び出し getSharingLinks(0, urls, response.entries); $("#loading-img").fadeOut('slow'); }) .catch(function(error) { console.error(error); }); // 関数を定義 function getSharingLinks(i, urls, entries) { dbx.sharingListSharedLinks({path: entries[i].path_display}) .then(function(response) { var url = response.links[0].url.replace("www.dropbox.com","dl.dropboxusercontent.com").replace("?dl=0",""); urls.push(url); i++; if ( i == entries.length) { return console.log(url); } else { // 再帰的に関数を呼び出し getSharingLinks(i, urls, entries); } }) .catch(function(error) { console.error(error); }); }
まとめ
Javascriptで非同期の処理を扱う際は、Promiseやasyncなどを使うのが基本みたいですが、ちょっと調べただけではよく理解できなかったので、今回はきれいではないですがとりあえずこんな感じで解決しました。 いつか必要になると思うのでasyncについてもいつか勉強しようと思います。
heroku container:push でエラー
先日からherokuCLIをアップデートしたあたりからherokuにデプロイしようとする度に以下のエラーが出るようになってしまいました。
(node:26614) Error Plugin: heroku-container-tools: files attribute must be specified in /Users/username/.local/share/heroku/node_modules/heroku-container-tools/package.json module: @oclif/config@1.6.27 plugin: heroku-container-tools root: /Users/username/.local/share/heroku/node_modules/heroku-container-tools See more details with DEBUG=* (node:26614) Error Plugin: heroku-container-tools: files attribute must be specified in /Users/username/.local/share/heroku/node_modules/heroku-container-tools/package.json module: @oclif/plugin-legacy@1.0.15 plugin: heroku-container-tools root: /Users/username/.local/share/heroku/node_modules/heroku-container-tools See more details with DEBUG=* Uninstalling heroku-container-tools... done
エラーメッセージを読んでもよくわからなかったのでいろいろ検索したところ以下の記事がヒットしました。
Latest Docker update broken Heroku cli?
症状は違いますがheroku-container-tools
が悪さをしているようだったので、以下コマンドでアンインストールし、heroku-container-registry
をインストールし直したところ、エラーは出なくなりました。
$ heroku plugins:uninstall heroku-container-tools $ heroku plugins:install heroku-container-registry
Node.jsの環境をDockerで構築&herokuにデプロイ
はじめに
それぞれ他サイトを参照して作ったのみですが、自身の備忘までに。
会社でSkyway使うかも?という話が出てきたため、検証環境を作るのが目的です。 * 他にもいろいろパッケージ入れて検証するかもーついでにnodejsとかの勉強もしなきゃ * 最近Docker触ってないしDockerで環境つくりたい * というかherokuにDockerのコンテナをデプロイできるようになったみたいだし使ってみたい ということで、node.jsの環境をDocker上に構築し、それをHerokuにデプロイするまでを試してみたいと思います。
Skywayとは?
NTT Comunicationsが開発している、WebRTCのためのプラットフォームです。
公式サイト
Enterprise Cloud Skyway
SkyWayとは
ビデオ会議やコンタクトセンター、遠隔作業支援、オンライン教育、ライブ配信など、さまざまな機会において、オンラインでのリアルタイムコミュニケーションのニーズが高まっています。 ビデオ・音声通話、データ通信といったリアルタイムコミュニケーションの標準技術である「WebRTC」が登場し、リアルタイムコミュニケーションがより実現しやすくなってきました。 SkyWayを利用すれば、WebRTCに必要なサーバを構築・運用することなく、手軽にビデオ・音声通話、データ通信を利用できます。 自社サービスの開発・提供に専念して、イノベーションに集中することができます。
要は、Skywayを使えばWebRTCのアプリが簡単に開発できるというものです。
WebRTCはクライアント間で直接通信する方法ですが、そのためにはシグナリングサーバーと呼ばれる、クライアント間の接続する仲介をするサーバーを準備する必要があるのですが(これがまた複雑、らしい)、Skywayではそういったシグナリング等の、WebRTCに必要なサーバサイドの機能をPFとして公開してくれているため、我々開発者はクライアント側の実装に集中できるのです。
自分もまだそんなに詳しくないのでこの辺で。。
Node.jsのDocker環境
まずはDocker上の環境を作っていきます。
とはいえこれまでほとんどNode.jsなんて触ったことがなく全く知識が無いため、 とりあえず先人の行いに習って作ってみます。
以下を参考にしました。
【初心者向け】Dockerで手軽にNode.js開発環境構築 (2)
参考というかほとんど完コピだけど。。
以下のDockerfile
とdocker-compose.yml
を作成
FROM node:8.9.4-alpine ENV NODE_ENV=development RUN npm install -g express-generator@4.15.0 WORKDIR /app EXPOSE 3000
version: '3' services: webserver: build: ./ image: node-express-dev:1.0 container_name: node tty: true volumes: - ./:/app ports: - "8080:3000"
ファイルの配置は記事とは変えてすべて同じディレクトリにしています。
MyApp ├─ Dockerfile └─ docker-compose.yml
そしてビルドと起動
$ docker-compose build $ docker-compose up -d
コンテナに接続し、express-generator
でアプリの雛形を作成します。
参照先サイトでは、オプションにpug
を指定しています。
pug
はテンプレートエンジンですが、構文が馴染めなそうだったので、今回はhtmlライクに書けそうなejs
を指定しました。
$ docker exec -it node /bin/sh # express -f --view=ejs /app # npm install
雛形が生成され、こんな構成になりました。
MyApp ├── Dockerfile ├── app.js ├── bin │ └── www ├── docker-compose.yml ├── node_modules ├── package-lock.json ├── package.json ├── public │ ├── images │ ├── javascripts │ └── stylesheets ├── routes │ ├── index.js │ └── users.js └── views ├── error.ejs └── index.ejs
以下コマンドで開発用サーバを起動します。
# npm start
http://localhost:8080
をブラウザで開いてExpressのスタートページが表示されれば成功です。
Dockerコンテナ上ではポート3000番で立ち上がっていますが、ブラウザの8080番ポートが3000番に転送されています。
Skywayのチュートリアル
さて、とりあえずnode.jsとWebサーバが立ち上がる環境はDocker上に構築出来たので、次はいよいよskywayのチュートリアルアプリを立ち上げていきます。
Javascript SDKの概要は以下公式ページに記載があります。
Skyway JavaScript SDK チュートリアル
とりあえず、今回はまず動くものを動作させたいので、チュートリアルのコードをそのまま使います。
Github上に公開されているサンプルコードを使用します。
今回はP2Pの1対多通信のものを使用しました。
github : skyway/skyway-js-sdk/examples/p2p-broadcast
先程構築したnode環境ではview/index.ejs
がホームにて表示されるページのテンプレートになっています。
↑のgithubに上がっていたindex.html
の内容をそのままvies/index.ejs
に貼り付けます。
script.js
、style.css
はそれぞれ、public/javascripts/script.js
、pubic/stylesheets/style.css
に配置します。
また、APIキーをSkywayのダッシュボードから取得し、public/javascripts/key.js
に記載します。
javascript
window.__SKYWAY_KEY__ = '<YOUR_KEY_HERE>'
index.ejsの読み込み先のパスも変更
<head> <meta charset="utf-8"> <title>SkyWay - Broadcast example</title> <link rel="stylesheet" href="/stylesheets/style.css"> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript" src="//cdn.webrtc.ecl.ntt.com/skyway-latest.js"></script> <script type="text/javascript" src="/javascripts/key.js"></script> <script type="text/javascript" src="/javascripts/script.js"></script> </head>
ここまで出来れば、http://localhost:8080
にてSkywayのサンプルが動作するはずです。
browserifyの導入
一旦ここまでで、Skywayの動作確認は出来ますが、jQueryやSkywayのJavascript SDKをCDNから読み込んでいます。
ここままでも問題ないのですが、後々いろんなパッケージを導入することを考えると、(今回もWebRTCでの配信→音声認識とかを試したいなーと思ってるので)
パッケージのバージョン管理が大変になったり、何よりも今回はnode.jsの環境構築の勉強も兼ねていたので、
* npmでパッケージインストール
* browserifyでコンパイル
という流れでパッケージ管理ができるよう変更したいと思います。
まずはnpmでjQueryとskyway-jsをインストールします。
# npm install jquery --save # npm install -D browserify
パッケージをインストールしたら、script.js
内でそれらのライブラリをrequire
する分を記載します。
'use strict'; // Imports jQuery const $ = require('jQuery'); $(function() { // Imports the skyway library const Peer = require('skyway-js');
読み込みも簡単ですね。
ただしこれだけだとrequire
をブラウザが解釈できないので、動作しません。
browserify
でコンパイルする必要があります。
一旦上記script.js
はscript/
ディレクトリを作成し、その配下で管理、開発、コンパイルしたらpublic/javascripts/script.js
が生成されるようにします。
コマンドは以下。
# $(npm bin)/browserify script/script.js -o public/javascripts/script.js
これでpublic/javascripts/script.js
がコンパイルされた形で生成されました。
再びnpm start
コマンドでサーバを立ち上げ、http://localhost:8080
をブラウザで開いてExpressのスタートページが表示されれば成功です。
Herokuへのデプロイ
ひとまず、Docker上に環境を作ることができたので、いよいよHerokuの環境にデプロイをしていきます。
以下のサイトを参考にしました。 * Container Registry & Runtime (Docker Deploys) * Heroku で Docker を使う場合の諸注意
herokuにDockerコンテナをデプロイするためには以下の注意点があるみたいです。
- PORT 環境変数で Listen する Webアプリケーションであること
- Network link は未サポート
- Default working directory = '/'。変更するときは WORKDIR を指定する。
- CMDが必須。ENTRYPOINTはオプション。
- VOLUME、EXPOSE、STOPSIGNAL、SHELL、HEALTHCHECKは未サポート
今回のサンプルはPORT
が3000で動作し、起動コマンドはnpm start
なので、以下をDockerfileに追記します。
ENV PORT 3000 CMD ["npm", "start"]
あとは以下コマンドでHerokuにデプロイします。
$ heroku login # herokuにログイン $ heroku container:login # Heroku 上の Container Registry へログイン $ heroku create # Heorkuアプリの作成 $ heroku container:push web # コンテナをにContainer RegistryにPush # 2018/5/15の変更にて以下のコマンドが必要になった $ heroku container:release web # heroku アプリをデプロイ
heroku open
でherokuアプリがブラウザで開けば成功です。
※5/15時点で変更があり、heroku container:push web
だけではherokuアプリにはデプロイされなくなったようです。実際にアプリにデプロイするにはheroku container:release web
を実行する必要があります。
Pushing images to Container Registry, either via the heroku container:push CLI command or using docker push, no longer releases those images to your application. To create a new release using the images pushed to Container Registry, run heroku container:release (specifying the process types you would like to release). Separating push and release allows you to:
- Push several images and then release them all at the same time.
- Use the release phase feature (run tasks before a new release of your app is deployed).
- Push one image and release it to multiple process types with different CMDs, via heroku.yml or API.
Pushing images to Container Registry no longer creates a release
まとめ
Herokuはこれまでもとても便利だったので大変重宝しておりましたが、自分自身余りgitを使わないし、グローバル環境で動作試験をしたいときもわざわざgit commit
してログに残るのが嫌だったので、Dockerコンテナを気軽にデプロイ出来るのはとても便利だなーと思いました。
参考サイト
プライバシーポリシー
こんにちは管理人のti_takaです。
下記、「プライバシーポリシー」に関して記載致しましたので、ご一読願います。
当サイトに掲載されている広告について
当サイトでは、第三者配信の広告サービス(Googleアドセンス、もしもアフィリエイト)を利用しています。 このような広告配信事業者は、ユーザーの興味に応じた商品やサービスの広告を表示するため、当サイトや他サイトへのアクセスに関する情報 『Cookie』(氏名、住所、メール アドレス、電話番号は含まれません) を使用することがあります。 またGoogleアドセンスに関して、このプロセスの詳細やこのような情報が広告配信事業者に使用されないようにする方法については、こちらをクリックしてください。
当サイトが使用しているアクセス解析ツールについて
当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。
このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。
このトラフィックデータは匿名で収集されており、個人を特定するものではありません。
この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。
この規約に関して、詳しくはこちら、またはこちらをクリックしてください。
当サイトへのコメントについて
当サイトでは、スパム・荒らしへの対応として、コメントの際に使用されたIPアドレスを記録しています。
これはブログの標準機能としてサポートされている機能で、スパム・荒らしへの対応以外にこのIPアドレスを使用することはありません。
また、メールアドレスとURLの入力に関しては、任意となっております。
全てのコメントは管理人であるti_takaが事前にその内容を確認し、承認した上での掲載となりますことをあらかじめご了承下さい。
加えて、次の各号に掲げる内容を含むコメントは管理人の裁量によって承認せず、削除する事があります。
- 特定の自然人または法人を誹謗し、中傷するもの。
- 極度にわいせつな内容を含むもの。
- 禁制品の取引に関するものや、他者を害する行為の依頼など、法律によって禁止されている物品、行為の依頼や斡旋などに関するもの。
- その他、公序良俗に反し、または管理人によって承認すべきでないと認められるもの。
免責事項
当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。
権利を侵害する目的ではございません。
記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接メールでご連絡下さい。
確認後、対応させて頂きます。
当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。
当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。
当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。
プライバシーポリシーの変更について
当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。
修正された最新のプライバシーポリシーは常に本ページにて開示されます。
運営者:ti_taka