Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 9044 articles
Browse latest View live

AWS LambdaでNODE_MODULE_VERSION系のエラーが起きたときの早見表

$
0
0

こういうエラーに遭遇することがある。

Something went wrong installing the "sharp" module

The module '/opt/nodejs/node_modules/sharp/build/Release/sharp.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 57. This version of Node.js requires
NODE_MODULE_VERSION 64. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).

- Ensure the version of Node.js used at install time matches that used at runtime
- Consult the installation documentation at https://sharp.pixelplumbing.com/en/stable/install/
- Search for this error at https://github.com/lovell/sharp/issues

NODE_MODULE_VERSIONはどこでみる?

ここで確認できる。
https://nodejs.org/ja/download/releases/

AWS Lambdaの場合

それぞれ以下のruntimeになる。

  • NODE_MODULE_VERSION 57: nodejs8.10
  • NODE_MODULE_VERSION 64: nodejs10.x
  • NODE_MODULE_VERSION 72: nodejs12.x

JWTクレーム・セット部分のデコード方法

$
0
0

概要

JavaScriptで、正確にはNode.jsで、IDトークンのペイロード部分をサクッとデコードしたかった・・・・んだけど、しょうもないところででハマッたので、メモ。

以下を目的とする。

  • Node.jsの標準ライブラリの範囲で、JWTのペイロード部分を日本語を含めてデコードする

なお、「標準のライブラリで」のこだわりが無ければ、jsonwebtokenとかjwt-decodeとかのライブラリを使うのが吉。

検証環境

Node.js - v10.16.0

JWTペイロード部分(クレームセット)をデコードするスクリプト

var atob = (base64) => {
    var buffer = Buffer.from( base64, 'base64' );
    var utf8 = buffer.toString('utf8'); // Not "ascii"
    return utf8;
};
var decodeJwt = (token) => {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');

    var encodeURI = encodeURIComponent(atob(base64));
    var decodeString = decodeURIComponent(encodeURI);
    return JSON.parse(decodeString);
};

引数に与えてコマンドラインから変換するなら次のようにする。引数「JST_STRINGS」に入れるべきは、例えばOpenID ConnectでのトークンEndPointから取得したJSONのid_tokenキーに格納された文字列など。

decode_jst.js
var atob = (base64) => {
    var buffer = Buffer.from( base64, 'base64' );
    var utf8 = buffer.toString('utf8'); // Not "ascii"
    return utf8;
};
var decodeJwt = (token) => {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');

    var encodeURI = encodeURIComponent(atob(base64));
    var decodeString = decodeURIComponent(encodeURI);
    return JSON.parse(decodeString);
};

var main = (argv) => {
    if( argv.length < 3 ){
        console.log("\n  [USAGE] node decode_jst.js JST_STRINGS\n");
        return;
    }

    var jsonObj = decodeJwt(argv[2]);
    console.log(jsonObj);

    return;
};

main(process.argv);

※encodeURIComponent()は要らんかも?あとで仕様をちゃんと確認しよう【ToDo】

補足

ブラウザのJavaScript環境でデコードする場合は、windowsオブジェクト(配下のatob関数)を用いて、以下の記事の方法で出来る。

参考サイト

ラズパイとサーモセンサーで自宅のガスコンロ監視(火の用心)

$
0
0

■はじめに

キッチンで煮物をしながらうっかり外出。子供がガスを点けたまま放置。などなど火の元の安全はいつも気になりますね。
これから外出しようというときや、大人が自宅にいないのにガスコンロが使われていたら、スマホにアラートを通知してくれるとちょっとだけ安心。。。そんな機能をおうちハックの一環で作ってみました。
ラズパイにサーモセンサーをつないでガスコンロの状態を監視し火の元の状態を常に保持。スマホの位置情報や自宅 WIFI 接続情報、自宅の人感センサーから在宅かどうかを判別、不在になるときにガスコンロで火が使われていたら LINE に通知します。
これがあれば完璧というものではありませんがもしもの時は少しは役立つかも?。

■全体図

gus.png

ラズパイにサーモセンサーを接続し定期的にガスコンロの状態を監視するコマンドを cron で定期サービス化、同時にガスコンロ周辺の様子をWEBカメラで撮影。任意のタイミングでラズパイから LINE Messaging API を使って家族スマホの LINE にアラートメッセージとキッチンを撮影した画像を送信しています。

■サーモセンサーの使い方

102382388[1].JPG
ラズパイからのサーモセンサーの制御などは以下の記事を参考にさせていただきました。
使用しているセンサーも以下記事同様、楢ノ木技研の [USBサーモグラフィモジュール2]です。

参考:サーモカメラとRaspberry Pi で熱源監視

※上記記事ではラズパイとサーモセンサーはシリアル通信で接続のため多少工作していますが、うちではそのままラズパイにUSBで接続して以下の公式サンプルコードを利用しています。

ソースコード:
楢ノ木技研から公開されている以下の Linux 用のコードがそのままラズパイに USB 接続したサーモセンサーで使えます。
https://github.com/yamamaya/libUSBThermo

実行すると以下のような感じで横16x縦4の解像度でセンサー正面の温度を測定してくれます。(以下だとセンサー正面左上の位置で138度の熱源があることがわかります)
解像度は荒いですが、比較的遠くまで検出できるようで(うちでは 2m くらい先の温度までちゃんと取れていました)カメラの画像とは違うのでこのくらいの解像度でも今回の目的では十分です(もっと高解像度のセンサーはとても高価になります)

USBThermoRead:
138.9 119.8 132.6 62.3 35.7 33.7 32.5 33.0 33.3 32.8 32.6 34.3 33.7 34.3 34.6 35.3
127.8 96.8 102.7 51.0 33.9 32.8 32.9 32.3 32.7 33.1 33.5 32.6 32.4 33.1 32.9 34.7
73.4 71.8 53.4 35.7 39.2 32.3 33.7 31.6 33.2 33.0 32.6 34.3 33.5 33.4 33.8 34.5
37.6 36.1 34.7 37.3 35.4 32.9 32.6 32.4 32.4 32.0 32.5 31.7 32.8 34.2 34.9 34.6

■WEB カメラの使い方

ガスコンロ・キッチンの様子の撮影には市販の安価な WEB カメラを使用しています。
WEB カメラは UVC(USB Video Class)に対応しているものであれば、たいていの物はラズパイで使用可能ですのでオークションなどで出回っている安物カメラで十分です。
静止画の撮影には今回は fswebcam というコマンドを使用しました。

参考:RaspberryPi 人感センサーからカメラ撮影してメール送信しちゃう
参考:安価なWebカメラC310をUSBでラズパイに接続して画像を取得する

■LINE Messaging API の使い方

LINE Messaging API の使い方は以下で詳細に解説していますので参考にしてください。

投稿記事:[LINE と Firebase とラズパイを繋いでみたよ☆ - Qiita]

■実際の利用例

上記の組み合わせでガスコンロの状態監視と、キッチンの様子を撮影した画像、LINEへの通知が行える状態になりますが、実際どのようなタイミングで通知を行っているか実際の使用例のご紹介です。

●家族の誰かが外出したタイミング
家族それぞれのスマホに IFTTT による外出通知機能を設定しています。自宅周辺から一定距離離れると、IFTTT に通知が飛び、そこからラズパイで自作の自宅スマートホームサーバーに WEBHOOK API で通知を転送しています。
自宅のサーバーでこの通知を受け取ったタイミングで、もしガスコンロに火がついていた場合には即座に家族全員の LINE に火の元の警告を通知します。

●無人の時
自宅キッチンのラズパイに人感センサーを接続して自宅に人がいるか常に監視しています。
一定時間以上 家が無人の状態の時に、もしガスコンロに火がついていた場合には即座に家族全員の LINE に火の元の警告を通知します。

■最後に

今回はちょっといつもの家電操作のお気軽 おうちハック とは違て、少しクリティカルなシチュエーションでの利用です。
この記事で書かれていることだけで火の元の安全が保証されるわけではないのでくれぐれもご注意ください。
実際にこの機能を必要とするような状況が発生しないほうが安心という意味では、ちょっとこれまでとは違った方向性の機能ですが、ちょっとした安心感のためという程度です。
この手のリスクのある領域は遠隔操作や自動化はリスクと隣り合わせです。誤動作した時の被害も大きいので直接火を制御するようなインプリは避けるべきですし、今回のような監視のみでも過信は禁物です。

※投稿記事一覧

・GoogleHome/AmazonEcho とラズパイでやった事・やりたい事一覧[LINK]
・Google Home でちょっと未来風のスマート TV を作ってみたよ☆[LINK]
・黒豆 (Broadlink RM Mini 3) の IR 信号解析してみたよ♪ [LINK]
・スマートロック SESAME の WEB API が便利だった![LINK]
・Amazon Echo の沢山ある面白スキルを気軽に遊ぶ方法[LINK]
・LINEを「ラズパイのターミナル化&スマートホームのフロントエンド化(ChatOps風)」してみた♪[LINK]
・LINE と Firebase とラズパイを繋いでみたよ☆[LINK]
・GoogleHome と Chromecast でキーワード&画像&Wikipedia検索~TV表示[LINK]
・スマホ ハック&スマートホーム連携 [LINK]
・ラズパイとサーモセンサーで自宅のガスコンロ監視(火の用心) [LINK]

lambda+API Gateway構成を作ってみる

$
0
0

lambdaとAPI辺りの学習です。
外部APIをコールするlambdaをコールするAPI(via API Gateway) を意味もなく作る備忘録。
それだけだとつまらないので天気予報を取得し、取ってきたjsonをそのままDynamoDBに格納する構成を作る。

contents

  • Node.jsでAPIをコールする方法。
  • AWS環境でAPI Gateway + lambdaの設定方法。
  • ローカル環境でAPI Gateway + lambdaの設定・実行方法。

事前準備

  • OpenWeatherにSign UpしてAPIキーを入手。
  • AWS SSM パラメータストアへURIとキーを登録。

SSMとDynamoDBのSDK(Node.js)の使い方

Node.jsからパラメータストアを参照

constAWS=require('aws-sdk');constssm=newAWS.SSM();constres=ssm.getParameter({Name:"パラメータ名",WithDecryption:false});//WithDecryptionは復号化の有無

Node.jsからDynamoDBへ書き込み

constAWS=require('aws-sdk');constdocumentClient=newAWS.DynamoDB.DocumentClient({region:'ap-northeast-1'});constparams={TableName:'テーブル名',Item:{'属性1':'値1','属性2':'値2'}}documentClient.put(params,(err,data)=>{if(err)console.log(err);elseconsole.log(data);});

API Gatewayの設定

コンソールから作成する場合

  1. REST APIを選択。
  2. API名を新規作成。対象となるlambdaを選択して指定。
  3. [アクション]からメソッドの作成→POST
  4. テスト⚡︎をクリックし、POSTするJSONメッセージを入力したらテストボタン押下。右側にレスポンスが表示される。なお、このままデプロイしてURIを発行しても良いが、この状態だとどこからでもアクセスし放題なためセキュリティ的に不安。
  5. サイドバーの[リソース]から[メソッドリクエスト]を選択。APIキーの必要性をtrueに。
  6. [アクション]からAPIのデプロイを選択→ステージ名を選択or新規作成。→URIが発行される。
  7. サイドバーの[APIキー]からAPIキーを作成する。命名するのみ。
  8. サイドバーの[使用量プラン]を選択し、関連付けられたAPIステージに6で作成したAPI名とステージ名を設定。APIキーに8で作成したAPIキーを設定する。
  9. postmanからPOST。キーを設定しないと{"message":"Forbidden"}が返り、x-api-keyに指定して投げるとDynamoDBに記録され成功する。

DynamoDBの設定

AWS CLIから作成する場合

テーブル作成
$ aws dynamodb create-table --table-name'テーブル名'\--attribute-definitions'[{"AttributeName":"プライマリキー名","AttributeType": "S"}]'\--key-schema'[{"AttributeName":"プライマリキー名","KeyType": "HASH"}]'\--provisioned-throughput'{"ReadCapacityUnits": 5,"WriteCapacityUnits": 5}'

設定値についての参考:https://qiita.com/propella/items/75e1a68468e37c3ebf45

データ挿入
$ aws dynamodb put-item --table-nameテーブル名 \--item'{"date":{"N":"20200110"},"test":{"S":"TEST"}}'

なお、次のエラーが出るのは以下の場合が考えられます。

Error parsing parameter '--item': Expected: '=', received: '"' for input:

  • コマンドプロンプトから実行している場合。
    → シングルクオートを使うからいけないらしい。中身のダブルクオートをエスケープして、
    "{\"date\":{\"N\":\"20200110\"},\"test\":{\"S\":\"TEST\"}}"
  • 外側の{}を忘れている場合。

コード

index.js
constAWS=require('aws-sdk');constssm=newAWS.SSM();constdocumentClient=newAWS.DynamoDB.DocumentClient({region:'ap-northeast-1'});consthttps=require('https');consturl=require('url');constdate=newDate();constgetWeatherInfo=asyncfunction(latitude,longitude,context){constssmResultUri=awaitssm.getParameter({Name:"OPENWEATHER_URI",WithDecryption:false}).promise();constssmResultKey=awaitssm.getParameter({Name:"OPENWEATHER_APPID",WithDecryption:true}).promise();constopenWeatherUri=ssmResultUri.Parameter.Value;constappid=ssmResultKey.Parameter.Value;constendPoint=openWeatherUri+"?lon="+longitude+"&lat="+latitude+"&appid="+appid;constoptions=url.parse(endPoint);options.method='GET';options.headers={'Content-Type':'application/json'};constreq=https.request(options,(res)=>{res.on('data',async(chunk)=>{if(JSON.parse(chunk).cod==200){constparams={TableName:'weather_forecast',Item:{'date':date.getTime(),'forecast':JSON.parse(chunk)}};console.log("write DB");awaitdocumentClient.put(params,(err,data)=>{if(err)console.log(err);elseconsole.log(data);}).promise();context.succeed("success");}});});req.on('error',(e)=>{console.log('problem with request: '+e.message);context.fail(e.message);});req.end();};exports.handler=function(event,context){console.log("lambda start");getWeatherInfo(event.latitude,event.longitude,context);console.log("lambda end");};

localでAPIをテストする場合。

SAMでローカルにlambdaの実行環境を作成。

$sam init --runtime nodejs10.x

SAM CLI + Swagger

※ SAMからのapi-keyの設定は未対応らしい。
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-api-gateway-extensions.html

swagger.ymlファイルを作成しAPIを定義し、template.ymlから参照する。なお、DefinitionUriではなくDefinitionbodyを使うならtemplate.yml内に続けて記述可能。
(swaggerについては、コンソールから作成したAPIがあればエクスポートすることができるためそれをベースに改良するほうが楽)

swagger.yml
swagger:"2.0"info:description:"getweatherinfoandinserttoDynamoDB"version:"1.0.0"title:"get_weather_info"basePath:"/dev"schemes:-"https"paths:/:post:produces:-"application/json"responses:200:description:"200response"schema:$ref:"#/definitions/Empty"security:-api_key:[]x-amazon-apigateway-integration:uri:"arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:getOpenWeatherInfo/invocations"responses:default:statusCode:"200"passthroughBehavior:"when_no_match"httpMethod:"POST"contentHandling:"CONVERT_TO_TEXT"type:"aws"definitions:Empty:type:"object"title:"EmptySchema"
template.yml
AWSTemplateFormatVersion:'2010-09-09'Transform:AWS::Serverless-2016-10-31Description:call open weather API functionGlobals:Function:Timeout:10Resources:getOpenWeatherInfo:Type:AWS::Serverless::FunctionProperties:CodeUri:getOpenWeatherInfo/Handler:index.handlerRuntime:nodejs10.xRole:'arn:aws:iam::xxxxxxxxxxxx:role/getWeatherInfoRole'Events:Api:Type:ApiProperties:Path:/devMethod:postgetWeatherInfo:Type:AWS::Serverless::ApiProperties:StageName:devDefinitionUri:swagger.yml

ローカルのlambdaをAPIコールするにはAWS::Serverless::Apiの設定ではなくAWS::Serverless::FunctionEvents:以下が重要。これがないと以下のエラーを生じる。

Error: Template does not have any APIs connected to Lambda functions

ローカルにAPIを立てる

$ sam build
$ sam local start-api
Mounting getOpenWeatherInfo at http://127.0.0.1:3000/dev [POST]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-01-11 14:31:44  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

※追記

実際同じコードを用いてローカルのAPIを呼ぶと以下のエラーが出る。指示通りステータスコード等を含むオブジェクトをreturnしてあげると解決する。
localでやる場合とAWS環境でやる場合でレスポンスに求められる制限が違うのだろうか?

Function returned an invalid response (must include one of: body, headers, multiValueHeaders or statusCode in the response object). Response received: null

Firebase Hosting でのサイト公開手順メモ

$
0
0

Firebaseで導入時に操作する手順を残しておきます。
firebase公式サイト

0. 事前準備

以下はすでに導入済みのものとして説明は省略します。

  • node.jsのインストール
  • ブラウザからfirebase公式サイトへログインしてプロジェクト作成

1. firebase tool(firebase CLI)のインストール

下記のコマンドでインストールします。

npm install -g firebase-tools

インストール終わったら以下のコマンドで、firebase CLIから使えるコマンド一覧が確認できます。

firebase

こちらはバージョン確認

firebase --version

2. firebaseへログイン(Googleアカウントへの紐付け)

インストール後Googleアカウントへと紐付けをおこないます。

$firebase login

英語での質問が表示されますのでそのままEnterキーをクリックしてください。
するとブラウザでのログイン画面が立ち上がるので、Googleアカウントでログインします。

ログインできたら、以下でfirebase上のプロジェクト一覧が確認できます。

$firebase projects:list

プロジェクトID確認できるやつ

$firebase list

3. 初期化

事前にフォルダを作成して移動しておいてください。

$mkdir フォルダ名
$cd フォルダ名

下記のコマンドで対象フォルダを初期化します。

$firebase init

プロジェクト指定まだの場合は指定するオプション

$firebase init --project=プロジェクトID名

コマンドライン上にどのサービスを使うのかなど質問がでてきますのでスペースで選択して答えていきます。

選択質問後はyesかnoで答えるいくつか質問がでてきますので答えていきます。

▼質問項目(確かこんな内容)

  • 公開するファイルを格納したディレクトリ(初期設定はpublic)
  • SPAかどうか(yesだとどのアクセスでも/index.html参照になる)
  • 常にindex.html上書きかどうか など

上記完了すると2つのファイルが生成されます。

  • .firebaserc
  • firebase.json

4. デプロイ

下記のコマンドでローカルにあるpublicフォルダ配下のファイルがfirebaseサーバ上にアップロードされます。

$firebase deploy

コンソール上に表示される、下記のようなURLで公開となります。
https://xxxx.firebaseapp.com

node 10 -> 12 バージョンアップしたら、npm installでnode-sass@4.11.0はエラーになる

$
0
0

Unsupported for node-sass@4.11.0

2019年10月に、node v12はついLTSになりました:clap::clap::clap:

それで、既存のシステムのnodeをバージョンアップしたら、↓のようなエラーになりました!

    Cannot download "https://github.com/sass/node-sass/releases/download/v4.11.0/linux_musl-x64-72_binding.node":

    HTTP error 404 Not Found

    Hint: If github.com is not accessible in your location
          try setting a proxy via HTTP_PROXY, e.g.

          export HTTP_PROXY=http://example.com:1234

    or configure npm proxy via

          npm config set proxy http://example.com:8080

v12はサポートしなくなったらしいので、node-sass@4.12.0にアップデートする必要があります。

npm install node-sass@4.12.0

これでエラーが消えます。

node v12 はどこが新しくなったのか

  • JavaScriptエンジンであるV8はNode.js v10のv6.8からv12のv7.4にバージョンアップした、そのためパフォーマンスは大きく変わりました

    • Asyncスタックトレース
    • JavaScriptパース高速化
    • Await高速化
    • 引数ミスマッチ高速化
  • ClassにPrivateFields定義できる
    #でプライベートフィールドを定義する。(例:#name
    この#nameはClass内にしか使えない。

  • HTTP parserはllhttpに変わりました

  • TLS1.3もサポートするようになった

nodebrewからnodenv(on anyenv)に切り替える(2020年1月版)+ありがちなトラブル

$
0
0

動機

nodebrewにて手動でプロジェクト毎にnodeのバージョンを切り替えるのが面倒になったので、.node-versionファイルで自動切り替えできるnodenvで管理しようと思いました。
ひと悶着あり、同じことが起こってる人の役にたつかもしれないので、全体の流れも記録しておくことにしました。その都度、遭遇するかもしれないトラブルシューティングも下のほうに記載しておきましたので、躓いたら見てみてください。

最初の状態

環境

  • Mac
  • ターミナル
  • zsh

nodeのバージョンなど

10系と12系のプロジェクトがあるので、都度切り替えている感じです。

$ node -v

v10.16.3
$ which node

/usr/local/bin/nodebrew

その他にインストールされていたnodeのバージョン

$ nodebrew ls  

v9.11.2
v10.16.3
v12.10.0
v12.12.0

npmのバージョンなど

$ npm -v

6.13.0
$ which npm

/Users/user-name/.nodebrew/current/bin/npm

nodebrewを消す

nodebrewがインストールされている状態だとバッティングの可能性があるようなので、思い切って削除します。
-rfつけてる rmは慎重に。

$ rm -rf ~/.nodebrew

ばっさり

.bash_profileで通しているパスを削除しておく必要があるらしい。

$ vi ~/.bash_profile

↓のようなのを消しておきましょう

export PATH=/Users/user-name/.nodebrew/current/bin:$PATH

確認する。これでエラーが返ってきたらばっちりのはず…

$ node -v

/usr/local/bin/node

おっと…?

とりあえずanyenv -> nodenvをインストールしてみる

Homebrewで可能でした

$ brew install anyenv

からの。
初期化?

$ anyenv init

# Load anyenv automatically by adding
# the following to ~/.zshrc:

eval "$(anyenv init -)"

.zshrcファイルに記述が必要らしいので。

$ vi ~/.zshrc

まだファイルがなかった場合は下記の1行だけを置いておく形に。

eval "$(anyenv init -)"

viじゃなくても大丈夫だし、環境によっては .bashrcとかに記述の必要があるかも。

どこかに書かれていたけど、以下も必要なのかな?

$ anyenv install --init

とりあえず都度シェルをきれいに。

$ exec $SHELL -l 

本題の nodenvのインストール。 anyenv経由でできるらしい。

$ anyenv install nodenv

(前略)
Install nodenv succeeded!
Please reload your profile (exec $SHELL -l) or open a new session.

おけ。指示通りターミナルを初期化して、今の状態を確認してみる。

$ exec $SHELL -l
$ nodenv versions

* system => v12.12.0 (set by /Users/user-name/.anyenv/envs/nodenv/version)

おぉ?まだ nodenvでnodeをインストールしてないけど…まぁええんか?

以前の状態に合わせるために古いnodeもインストール

$ nodenv install 10.16.3

nodenv versions        
* system => v12.12.0 (set by /Users/user-name/.anyenv/envs/nodenv/version)
  10.16.3

ちゃんと入ったっぽい。

マシンを3年も使っていると色々と溜め込んだものがありまして

手動で切り替えてみよう

$ nodenv global 10.16.3

(略)

$ node -v     

v12.12.0

あかん。なんでや?

どこのnodeを使っているか場所を確認。

$ which node

/Users/user-name/.nodebrew/current/bin/node

ゾンビnode発見。
もういないはずの、 nodebrewの文字が…

確認。

$ nodebrew

zsh: command not found: nodebrew

やはりゾンビ。
さよならだ。(正直謎…)

$ rm -rf /Users/user-name/.nodebrew

気を取り直して

$ node -v

v13.2.0

HAHAHA、冗談はやめてくれYO

$ which node

/usr/local/bin/node

地縛霊発見。

sudo rm -rf /usr/local/bin/node

お祓い完了。

node -v

zsh: command not found: node

ほぅ…

(少しやけくそ目に)

$ anyenv install nodenv

$ nodenv versions

* 10.16.3 (set by /Users/user-name/.anyenv/envs/nodenv/version)

$ node -v

zsh: command not found: node

おこ。




あ。もしや。

$ exec $SHELL -l
$ node -v

v10.16.3

いえーす。

いつから(ry

$ npm -v

6.13.1

$ which npm

/usr/local/bin/npm

お前もか

$ brew uninstall --ignore-dependencies npm

Uninstalling /usr/local/Cellar/node/13.2.0... (4,654 files, 58.8MB)
node 12.12.0 is still installed.
Run `brew uninstall --force node` to remove all versions.

$ node -v
v12.12.0

$ npm -v
6.13.1

$ which npm
/usr/local/bin/npm

お祓いがきかないぃ(まぁアンインストールされるバージョンも変なの出てるんですけどね…)
おこ。そろそろ慣れてきたけど。

$ which npm

/usr/local/bin/npm

$ rm -rf /usr/local/bin/npm
$ npm -v                   
6.11.3

$ which npm

/Users/user-name/.anyenv/envs/nodenv/shims/npm

(ファンファーレが頭の中で鳴り響く)

特定のバージョンのnpmが欲しかったので。

$ npm install -g npm@6.13.0

こうしておいて、手動でのnodeバージョンを切り替えてnpmもついてくるかテスト

$ nodenv global 12.12.0
$ node -v
v12.12.0

$ npm -v
6.13.0

$ nodenv global 10.16.3
$ node -v
v10.16.3

$ npm -v
6.9.0

できた

やっと本題の、 .node-versionでのローカルの管理

npmにしろnodeを用いる際にnode自体のバージョンによってエラーなどの原因になることがしばしばあります。nodebrewでの管理でプロジェクトごとにこの辺りの事情が異なる場合、プロジェクトをはしごする際に手動でのnodeの切り替えが必要になったりします。
nodenvはコマンドを実行するディレクトリに .node-versionファイルがあるとそのファイル内で指定されているバージョンのnodeを用いて各種コマンドを実行してくれるので、切り替えを意識することがなくなります。

といってもファイルを自分で書く必要はない

$ cd /path/to/project/root
$ nodenv local 12.12.0

これでそのディレクトリに .node-versionファイルを生成してくれ、次回以降このディレクトリ内での作業時には自動的にnodeのバージョンが切り替わります。

.gitignore_globalでignoreしておく

プロジェクトで固定、かつ、チームメンバーが nodenvを使っている前提であれば、プロジェクトのルートに .node-versionファイルをコミットしておいて、共有しておくのはとてもいいソリューションだと思いますが、諸々の事情でコミットできない場合もあると思います。そういう場合には、そのマシン全体でgitignoreできる .gitignore_globalを使うのもひとつの手です。

$ cd ~
$ vi .gitignore_global

ただし、その後、他のプロジェクトで .node-versionを使うとなった際にgitに入ってこなくなるので注意が必要です。

おかしい時のチェック

とりあえずバージョンは逐一確認してみる

node -v

インストールされている場所も確認しておく

which node

例えば、↓こういうのが表示されるのはMacのデフォルトのnodeを使っている状態なので、 nodebrewにしろ nodenvにしろが動作している前提だとしたら、間違っている可能性が高いです。

/usr/local/bin/node

こういう場合、nodenvなどのセットアップが適切にできればそちらが使われますし、最悪この元の場所にインストールすることもできますから、消しちゃって大丈夫です。

コマンドがみつからない系

* : command no found

とりあえず、下記でターミナルを再読み込みしてみたり、なんなら再起動してみましょう。割とそれで解決することが多いはず。

exec $SHELL -l

それでもダメな場合は所謂「パスが通っていない」可能性もあるので、インストール時に表示されていた内容を見直してみたり、再インストールする必要があるかもしれません。
install complete的なメッセージが出ていても、もうひと手順あることが意外とあります。

まとめ

やはり先にnodeを全部消すのがよいです。変に保険をかけずnode -vでなにも出なくなるようにするのを優先した方が早かったと思います。(もしかするとnpm -vも。)

Node.js module管理の仕組み

$
0
0

Node.jsデザインパターンを読んで勉強中なので
そちらの備忘録として投稿していきます。

まず初めにNode.jsがどうやって異なるバージョンの同一モジュールを管理し、
「依存地獄(dependency hell)」を回避しているかを整理します。

Node.jsの依存解決アルゴリズム

Node.jsがモジュールを読み込む時に
指定されたモジュール名をもとにモジュールをコアモジュール、もしくはローカルファイル
から探すために使用されるアルゴリズムは
以下の三段階に分かれている。

  1. コアモジュール
    まずは指定されたモジュール名がNodeのコアモジュールかどうか調べる

  2. ファイルモジュール
    コアモジュールに見つからなかった場合、ローカルファイルシステムを探す。
    モジュール名が「/」で始まる場合は絶対パスとして、「./」、「../」で始まる場合は
    requireを呼び出しているファイ ルからの相対パスとして解釈される。

  3. パッケージモジュール
    モジュール名の開始文字列が「/」、「./」、「../」のいずれでもない場合は、
    requireを呼び出しているファイルと同じディレクトリの下にあるnode_modulesディレクトリの中を探す。

それでも見つからない、もしくはnode_modulesディレクトリがなかった場合
さらに親のディレクトリを探しにいき、上へ上へ探索していき
ローカルファイルシステムのルートに到達するまで探す。

ファイルモジュールとパッケージモジュールのロード時のルール

ファイルモジュールとパッケージモジュールのロード時には
上記のアルゴリズム以外にルールがある。

  • 指定されたモジュール名と同じ名前のファイルがあれば (なければ拡張子.jsもしくは.jsonを補完して確認) そのファイルをロードする。
  • 指定されたモジュール名と同じ名前のディレクトリがあれば、その配下にpackage.jsonファイルがないか調べる。 存在すれば、ファイル中のmainプロパティで指定されたファイルをロードする。
  • 指定されたモジュール名ディレクトリ配下にindex.jsというファイルがあればロードする。

整理

myApp/
├── foo.js
└─┬ node_modules
├── depA
|└── index.js
├── depB
├── bar.js
└── node_modules
└── depA
└── index.js
└── depC
├── foobar.js
└── node_modules
└── depA
└── index.js
上記のようなディレクトリ構成として
各ファイルからモジュールdepAをロードしてみる。

  • /myApp/foo.jsからrequire('depA')を呼び出した場合
    /myApp/node_modules/depA/index.jsをロードする

  • /myApp/node_modules/depB/bar.jsからrequire('depA')を呼び出した場合
    /myApp/node_modules/depB/node_modules/depA/index.jsをロードする

  • /myApp/node_modules/depC/foobar.jsからrequire('depA')を呼び出した場合
    /myApp/node_modules/depC/node_modules/depA/index.jsをロードする

まとめ

依存解決アルゴリズムにより、Nodeは複雑な依存関係も解決でき、
ひいては大規模なアプリケーションにおいて、
バージョン間の衝突なく何百、何千といった依存パッケージをもつことが可能になる

参考書籍
Node.jsデザインパターン 第2版


ネットのない環境でWEBの実機テストをする

$
0
0

発端

筆者はよく行きつけのカフェでイキりながらWEB開発をしております。

ところが困ったことに、カフェなどで設置されている公共Wifiでは、ルーターの設定で他機の存在が秘匿にされている場合があります。(当然pingも打てない)

セキュリティの観点から妥当な処置ではあるのですが、開発しているWEBサイトをスマホで確認したい・・・といった場合には困ったことになります。

一つの解決策として、パソコン自体をルーターにして独自のLANを構築する、というものがあります。

Windowsでは7から標準でSoftAPという機能が用意されており、Microsoftが提供している仮想ネットワークアダプタを利用してパソコン単体でLANを構築できます。

今回は、パソコン単体でLANを構築 & Nodeでサーバーを起動、手持ちの端末でLANに接続してサーバーからレスポンスを受け取ることを目標にします。

LANの構築

1 . コマンドプロンプトを管理者権限で起動

2 . 以下のコマンドを実行

C: > netsh
netsh > wlan
netsh wlan > set hostednetwork mode=allow ssid=your_ssid key=your_password

3 . hostednetworkを起動

netsh wlan> start hostednetwork

これでうまくいった人はついてます。
筆者の場合は下記のログが出てLANが構築できませんでした。

netsh wlan> start hostednetwork
ホストされたネットワークを開始できませんでした。
グループまたはリソースは要求した操作の実行に適切な状態ではありません。

主にMicrosoft Hosted Network Virtual Adapterが有効になっていない場合が想定されます。対処法は後ろに載せます。参考に他の記事も載せておくのでそちらもどうぞ。

起動に成功した場合は、LAN内のipアドレスを確認します。

> ipconfing
~~
IPv4 アドレス . . . . . . . . . . . .: 192.168.0.1
~~

サーバーの起動

Nodeでサーバーを起動します。

server.js
constexpress=require("express");constapp=express()app.get('/',(req,res)=>{res.send(`Hello World!`);});app.listen(3000);

実機で接続

1 . 端末から、上で設定したSSIDにWifiで接続

2 . ブラウザで IPアドレス + ポート にアクセス
(上の例だと192.168.0.1:3000)

うまくHello Worldできてれば成功 !!

これでパソコンで起動したサーバーと端末を、ネット環境に依存せずに接続させることができました。

LANを構築しているパソコンがネットに接続できている場合は、端末がネット上のリソースを要求してもきちっと返してくれます。(というかまんまプロキシなので)

当然ネット環境がないとインターネット上のリソースが必要になるような処理は無理です。

しかし序盤で言ったように、他機の存在が秘匿されているようなLANでは、パソコンがプロキシとなって端末と接続できるようになる、という意味で有用な場面もあるのではないでしょうか。

(追記)Microsoft Hosted Network Virtual Adapterのインストール

SoftAPではMicrosoftが提供しているMicrosoft Hosted Network Virtual Adapterというネットワークアダプタを利用しています。こちらを用意してあげる必要があります。

1 . 下のコマンドを実行し、ドライバー名を確認する。

netsh wlan> show drivers

2 . デバイスマネージャーを起動し、ネットワークアダプターの欄の中にある、1で確認したドライバー名を右クリック

3 . ドライバーの更新を選択し、コンピューターを参照してドライバーソフトウェアを検索を選択。

4 . コンピューター上の利用可能なドライバーの一覧から選択しますを選択。

5 . Microsoftのモデルを選択して※次へ、ドライバーをインストールする。
※筆者の場合は同名のモデルがいくつもでて、順に更新していったら成功しました。

6 . デバイスマネージャーのメニューバーにある表示タブで、非表示のデバイスの表示を選択。ネットワークアダプターの欄にあるであろうMicrosoft Hosted Network Virtual Adapterを有効にする。

これでhostednetworkが起動できるはずです。

参考

【初学者向け】Herokuデプロイ時 code=H10 エラー解消

$
0
0

構築時に以下事象にて20分程はまってしまったので、備忘含め投稿します。

デプロイ時のエラー内容

#gitにコミットしたソースをHerokuへデプロイ
git push heroku master
#####アプリケーションエラー発生#####

#Herokuのエラーログを確認
heroku logs --tail

エラーログ

at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=XXXXX.herokuapp.com request_id=11089354-f9b9-4f65-a85f-c4969a772d76 fwd="XXX.XXX.XXX.XXX" dyno= connect= service= status=503 bytes= protocol=https
2020-01-11T17:36:56.788077+00:00 herok

結論

Package.jsonの記載誤り。

Package.json
{"name":"testheroku","version":"1.0.0","description":"","main":"index.js","start":"node index.js", ←ここに書かない"scripts":{"start":"node index.js", ←ここに書く"test":"echo \"Error: no test specified\"&& exit 1"},"author":"","license":"ISC","engines":{"node":"13.5.0","npm":"6.13.6"}}

お作法を色々と覚えていきたいものです。

fetchの呼び出し @Javascript & Node.js 実験室

$
0
0

fetchは、HTTP呼び出しで標準的に使っていますが、Content-Typeに従った呼び出し方をいつも忘れてしまうので、備忘録として残しておきます。

実動作の確認のためのソースコードを以下のGitHubに上げておきました。
 https://github.com/poruruba/fetch_laboratory

Javascriptからの呼び出しを中心に示しますが、同じコードがそのままNode.jsでも動作します。
ちょっとだけ、Lambdaでの動作も示しておきます。

呼び出し方法の種類

今回扱う呼び出し方法は以下の通りです。

・Get呼び出し
 HTMLのページ取得でおなじみです。パラメータをQueryStringに指定します。例えば、以下のような呼び出しです。
 http://localhost:10080/api?param1=abcd

・Post(Content-Type: application/json)呼び出し
 パラメータをBody部にJSONで指定するWebAPI呼び出しでは一番一般的ですね。

・Post(Content-Type: application/x-www-form-urlencoded)呼び出し
 フォームでSumitするときの方法です。例えば、以下のような呼び出しです。
 <form id="form" action="http://localhost:10080/post-urlencoded" method="post">

・Post(Content-Type: multipart/form-data)呼び出し
 フォームでmultipart指定でSumitするときの方法です。例えば、以下のような呼び出しです。
 <form action="http://localhost:10080/post-formdata" method="post" enctype="multipart/form-data">

Javascriptの場合

まずは、Javascriptから。

Post(Content-Type: application/json)呼び出し

functiondo_post(url,body){constheaders=newHeaders({"Content-Type":"application/json; charset=utf-8"});returnfetch(newURL(url).toString(),{method:'POST',body:JSON.stringify(body),headers:headers}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();});}

Javascriptから送信
image.png

Post(Content-Type: application/x-www-form-urlencoded)呼び出し

functiondo_post_urlencoded(url,params){constheaders=newHeaders({'Content-Type':'application/x-www-form-urlencoded'});varbody=newURLSearchParams(params);returnfetch(newURL(url).toString(),{method:'POST',body:body,headers:headers}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();})}

フォームから送信した場合
image.png

Javascriptから送信した場合
image.png

Post(Content-Type: multipart/form-data)呼び出し

functiondo_post_formdata(url,params){varbody=Object.entries(params).reduce((l,[k,v])=>{l.append(k,v);returnl;},newFormData());returnfetch(newURL(url).toString(),{method:'POST',body:body,}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();});}

フォームから送信した場合
image.png

Javascriptから送信した場合
image.png

GET呼び出し

functiondo_get(url,qs){varparams=newURLSearchParams(qs);varurl2=newURL(url);url2.search=params;returnfetch(url2.toString(),{method:'GET',}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();});}

フォームから送信した場合
image.png

Javascriptから送信した場合
image.png

Node.jsの場合

Node.jsでも呼び出し方は同じです。
ポイントは、以下のnpmモジュールを利用するところです。

・node-fetch
 https://github.com/node-fetch/node-fetch
・form-data
 https://github.com/form-data/form-data

以下を先頭に記述することで、ほぼJavascriptと同じ記述で同じ動作となります。

const FormData = require('form-data');
const { URL, URLSearchParams } = require('url');
const fetch = require('node-fetch');
const Headers = fetch.Headers;

実験のためのサンプルページ

以下のようなページから、各呼び出し方法を確認します。
F11を押して、DevToolsを開くと、通信内容を詳しく見ることができます。

image.png

先ほど示した関数群を呼び出しているだけでして、詳細はGitHubのソースコードを参照ください。
参考までに、リクエストを受け付けるサーバ側のソースも示しておきます。

index.js
'use strict';constHELPER_BASE=process.env.HELPER_BASE||'../../helpers/';constResponse=require(HELPER_BASE+'response');constRedirect=require(HELPER_BASE+'redirect');// Lambda+API Gatewayの場合に必要//const { URLSearchParams } = require('url');//const multipart = require('aws-lambda-multipart-parser');exports.handler=async(event,context,callback)=>{if(event.path=='/post-json'){console.log(event.body);varbody=JSON.parse(event.body);varresponse={path:event.path,param:{param1:body.param1,param2:body.param2,}};returnnewResponse(response);}elseif(event.path=='/post-urlencoded'){// Lambda+API Gatewayの場合はこちら//var body = {};//for( var pair of new URLSearchParams(event.body).entries() ) body[pair[0]] = pair[1];// swagger_nodeの場合はこちらvarbody=JSON.parse(event.body);varresponse={path:event.path,param:{param1:body.param1,param2:body.param2,}};returnnewResponse(response);}elseif(event.path=='/post-formdata'){// Lambda+API Gatewayの場合はこちら//var body = multipart.parse(event);// swagger_nodeの場合はこちらvarbody=JSON.parse(event.body);console.log(body);varresponse={path:event.path,param:{param1:body.param1,param2:body.param2,}};returnnewResponse(response);}elseif(event.path=='/get-qs'){console.log(event.queryStringParameters);varresponse={path:event.path,param:{param1:event.queryStringParameters.param1,param2:event.queryStringParameters.param2,}};returnnewResponse(response);}};

一方で、Node.jsでの呼び出しも確認したかったので、Node.js側の確認のためのソースも示しておきます。
動作としては、いったん、サーバ側でリクエストを受け付けたのち、パラメータで指定されたbody.typeの値によって、各呼び出し方法に従った呼び出しをNode.jsで行っています。
コードを見ていただくと、Javascriptでの呼び出しと同じであることがわかります。

'use strict';constHELPER_BASE=process.env.HELPER_BASE||'../../helpers/';constResponse=require(HELPER_BASE+'response');constRedirect=require(HELPER_BASE+'redirect');constFormData=require('form-data');constfetch=require('node-fetch');const{URL,URLSearchParams}=require('url');constHeaders=fetch.Headers;constbaseurl='http://localhost:10080';exports.handler=async(event,context,callback)=>{if(event.path=='/node'){console.log(event.body);varbody=JSON.parse(event.body);if(body.type=="post-json"){varparams={param1:body.param1,param2:body.param2,};returndo_post(baseurl+'/post-json',params).then(json=>{console.log(json);returnnewResponse({type:body.type,resposne:json});}).catch(error=>{console.log(error);returnnewResponse(error);});}elseif(body.type=="post-urlencoded"){varparams={param1:body.param1,param2:body.param2,};returndo_post_urlencoded(baseurl+'/post-urlencoded',params).then(json=>{console.log(json);returnnewResponse({type:body.type,resposne:json});}).catch(error=>{console.log(error);returnnewResponse(error);});}elseif(body.type=="post-formdata"){varparams={param1:body.param1,param2:body.param2,};returndo_post_formdata(baseurl+'/post-formdata',params).then(json=>{console.log(json);returnnewResponse({type:body.type,resposne:json});}).catch(error=>{console.log(error);returnnewResponse(error);});}elseif(body.type=='get-qs'){varqs={param1:body.param1,param2:body.param2,}returndo_get(baseurl+'/get-qs',qs).then(json=>{console.log(json);returnnewResponse({type:body.type,resposne:json});}).catch(error=>{console.log(error);returnnewResponse(error);});}varresponse={type:body.type,param:{param1:body.param1,param2:body.param2,}};returnnewResponse(response);}};functiondo_post(url,body){constheaders=newHeaders({"Content-Type":"application/json; charset=utf-8"});returnfetch(newURL(url).toString(),{method:'POST',body:JSON.stringify(body),headers:headers}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();});}functiondo_get(url,qs){varparams=newURLSearchParams(qs);varurl2=newURL(url);url2.search=params;returnfetch(url2.toString(),{method:'GET',}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();});}functiondo_post_urlencoded(url,params){constheaders=newHeaders({'Content-Type':'application/x-www-form-urlencoded'});varbody=newURLSearchParams(params);returnfetch(newURL(url).toString(),{method:'POST',body:body,headers:headers}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();})}functiondo_post_formdata(url,params){varbody=Object.entries(params).reduce((l,[k,v])=>{l.append(k,v);returnl;},newFormData());returnfetch(newURL(url).toString(),{method:'POST',body:body,}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();});}

補足

※レスポンスの型
最近レスポンスはJSONで返されることが多くなっていますので、すべてJSONで返ってくることを想定しています。もし、単なるテキストで返ってくる場合には以下の部分を変更します。


(変更前) return response.json();

(変更後) return response.text();

以上

NodeJSで、opencvを触るまで

$
0
0

Node jsは偉大ですよね!

一つのプログラムでhttpサーバーもwebscketサーバーも、ラズパイを使えばSPIやシリアル通信まで行えてしまいます!

しかも速度だってc++とそこまで変わらないし(ほんまか)

もうJavaScriptはクソだって言わせない!

さて、Nodejsを布教するのはこれくらいにして本題に入ります。

本題

Nodejsから、webカメラの画像を読み込みたい!!

調べていてよくあるのが、nodejsで建てたサーバーにクライアントから画像データを送るという記事。

しかし、私はnodejsをうごかしているパソコンにつながったカメラの映像を取得したいのです。

しかし、なかなか最適な答えにたどり着けなかったので、記事にまとめておきます。

手法

プログラムからカメラの画像を取得するといえば、やはりopenCVでしょう。

opencv3をサポートしているopencv4nodejsを使用します。

早速npmからインストール

npm install --save opencv4nodejs

これだけで導入完了です。
ただし、インストールにかなり時間がかかるので注意が必要です。

あとは他のモジュール同様、

const cv = require('opencv4nodejs');

すれば使用できます。

最後に

カメラ画像の取得の仕方と題しておきながら、本質情報がコード2行という記事になってしまいましたが、お許しください。

それでは、楽しいNodejsライフを!!

ネット環境なしで手持ちの端末をサーバーに接続させる。

$
0
0

発端

筆者はよく行きつけのカフェでイキりながらWEB開発をしております。

ところが困ったことに、カフェなどで設置されている公共Wifiでは、ルーターの設定で他機の存在が秘匿にされている場合が多いです。(当然pingも打てない)

セキュリティの観点から妥当な処置ではあるのですが、開発しているWEBサイトをスマホで確認する・・・といったことはできません。

対処法として、パソコン自体をルーターにして独自のLANを構築し、端末と通信することがあげられます。

パッと見では難しそうですが、Windowsでは7からSoftAPという機能が標準で用意されており、Microsoftが提供している仮想ネットワークアダプタを利用して、パソコンをルーターとして扱うことができるようになります。

そこで今回は、SoftAPを利用してパソコン単体でLANを構築し、Nodeでサーバーを起動、手持ちの端末でLANに接続して、サーバーからレスポンスを受け取ることを目標にします。

LANの構築

1 . コマンドプロンプトを管理者権限で起動

2 . 以下のコマンドを実行

C: > netsh
netsh > wlan
netsh wlan > set hostednetwork mode=allow ssid=your_ssid key=your_password

3 . hostednetworkを起動

netsh wlan> start hostednetwork

これでうまくいった人はついてます。
筆者の場合は下記のログが出てLANが構築できませんでした。

netsh wlan> start hostednetwork
ホストされたネットワークを開始できませんでした。
グループまたはリソースは要求した操作の実行に適切な状態ではありません。

主にMicrosoft Hosted Network Virtual Adapterが有効になっていない場合が想定されます。対処法は後ろに載せます。参考に他の記事も載せておくのでそちらもどうぞ。

起動に成功した場合は、LAN内のipアドレスを確認します。

> ipconfing
~~
IPv4 アドレス . . . . . . . . . . . .: 192.168.0.1
~~

サーバーの起動

Nodeでサーバーを起動します。

server.js
constexpress=require("express");constapp=express()app.get('/',(req,res)=>{res.send(`Hello World!`);});app.listen(3000);
# 実機で接続

1 . 端末から、上で設定したSSIDにWifiで接続

2 . ブラウザで IPアドレス + ポート にアクセス
(上の例だと192.168.0.1:3000)

うまくHello Worldできてれば成功 !!

これでパソコンで起動したサーバーと端末を、ネット環境に依存せずに接続させることができました。

LANを構築しているパソコンがネットに接続できている場合は、端末がネット上のリソースを要求してもきちっと返してくれます。(というかまんまプロキシなので)

当然ネット環境がないとインターネット上のリソースが必要になるような処理は無理です。

しかし序盤で言ったように、他機の存在が秘匿されているようなLANでは、パソコンがプロキシとなって端末と接続できるようになる、という意味で有用な場面もあるのではないでしょうか。

# (追記)Microsoft Hosted Network Virtual Adapterのインストール
SoftAPではMicrosoftが提供しているMicrosoft Hosted Network Virtual Adapterというネットワークアダプタを利用しています。こちらを用意してあげる必要があります。

1 . 下のコマンドを実行し、ドライバー名を確認する。

netsh wlan> show drivers
```
2 . デバイスマネージャーを起動し、ネットワークアダプターの欄の中にある、1で確認したドライバー名を右クリック

3 . ドライバーの更新を選択し、コンピューターを参照してドライバーソフトウェアを検索を選択。

4 . コンピューター上の利用可能なドライバーの一覧から選択しますを選択。

5 . Microsoftのモデルを選択して※次へ、ドライバーをインストールする。
※筆者の場合は同名のモデルがいくつもでて、順に更新していったら成功しました。

6 . デバイスマネージャーのメニューバーにある表示タブで、非表示のデバイスの表示を選択。ネットワークアダプターの欄にあるであろうMicrosoft Hosted Network Virtual Adapterを有効にする。

これでhostednetworkが起動できるはずです。

参考

【ECMAScript】importでSyntaxError

$
0
0

ECMAScriptの記法を学習していたところ、外部モジュールのimportで詰まったので解決策を残しておきます。

実行環境

  • Node.js : v12.12.0
  • IDE: WebStorm(2019.3.1)

ディレクトリ / ファイル

index.jsからadd.jsで定義した関数を使うだけの簡単なものです。

.├── add.js
└── index.js
index.js
import{add}from"./add.js";console.log(add(10,15));
add.js
exportfunctionadd(a,b){returna+b;}

なぜかimportの箇所SyntaxErrorとなる

実行時、エラーが出力されたので調べてみたところ、以下の対応が必要とのこと。

  • 実行時オプションに --experimental-modulesを指定する
  • ファイル拡張子を変更する(js -> mjs) or package.jsonを作成する

今回は拡張子は変えず package.jsonファイルを作成して対応。

(node:13761) ExperimentalWarning: The ESM module loader is experimental.
(node:13761) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
import { add } from "./add";^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Module._compile (internal/modules/cjs/loader.js:892:18)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
    at Module.load (internal/modules/cjs/loader.js:812:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
    at internal/main/run_main_module.js:17:11

エラー対応後

.├── add.js
├── index.js
└── package.json
package.json
{"type":"module"}

Node parameters--experimental-modulesを追加
webstorm.png

実行してみる

正常に実行できたようです。

/Users/xxxxx/.nodebrew/current/bin/node --experimental-modules /Users/xxxxx/WebstormProjects/untitled/index.js
(node:13854) ExperimentalWarning: The ESM module loader is experimental.
25

Process finished with exit code 0

参考

[Node.js の ESModules]
http://var.blog.jp/archives/80335431.html

import Vue from 'vue'の意味を研究して、分かってきたもの

$
0
0

import Vue from 'vue'はなぜ使えるか?

Vuejsの開発の際、よく最初に出てくるこれ

importVuefrom'vue';

下のような相対パスなどはまだわかりやすいけど、

importExamplefrom'./Example.vue';

vueはなずimportできるのか、みんな様こんな疑問はないのか。
もしかして俺一人の疑問かも😣

少し研究してきたことをまとめてみた。

importとは

Javascriptのモジュールシステムです。他のファイルを使いたい時、require functionで他のファイルの内容を引用する。
es6を使っている時、importrequireにコンパイルする。
だから、実際に実行するコードは以下。

importVuefrom'vue';⬇️constVue=require('vue')

nodejs引用ルール

実は、nodejsの中に、requireを使う時に、require.reslove functionを使ってファイルのパスを探している。このfunctionは優先順位は以下になる。
require(X) from module at path Y
1.もしXはコアのパッケージかどうか、普段のpathなどみたいは直接引用される。ではなければ、次へ。
2.もしX/から始めれば、YのパスはOSのrootと設定。ではなければ、次へ。
3.もしX./,/,../から始めれば、(Y + X)ファイルかディレクトリ引用。ではなければ、次へ。
4.node_modulesフォルダの中に、見つかる!

node_modulesにあるvue

node_modulesに確かにvueというフォルダは存在しているが、new Vue()などの操作の時は、JsのObjectとして使っている。では、なぜか!
真の犯人まではもう少し🎭❗️
npmのパッケージとして、内部の出力はまたルールがある!
まずは、node_modules/vueのディレクトリツリーを見てみる。

├── LICENSE
├── README.md
├── dist
├── package.json
├── src
└── types

ルールは以下になる。
1.package.jsonmainが定義されたら、mainに入り口を探す。
2.package.jsonがなければ、index.jsindex.nodeを読む。
3.どちらもなければ、Error: Cannot find module 'some-library'を投げる。

では、node_modules/vue/packeage.jsonこう書いてある❗️

..."main":"dist/vue.runtime.common.js",...

ではでは、だんだん明らかになってきた❗️

本当にいつもimportしたものは...

importvuefrom'vue';// 実は、これに変換するconstvue=require('./node_modules/vue/dist/vue.runtime.common.js');

その内容は

if(process.env.NODE_ENV==='production'){module.exports=require('./vue.runtime.common.prod.js')}else{module.exports=require('./vue.runtime.common.dev.js')}

これは普段npmのスクリプトにあるNODE_ENV=developmentを読んで、実行環境に応じて違うvueインスタンスのexportができるようになった💥

今回のimportルールや他にも今回出てなかったルールの詳細はnodejsのドキュメントに🤓


Laragonで簡単に開発環境を構築する

$
0
0

皆さん、学習するとき、開発をするとき、ローカルで確認を行うときの環境構築はどうされていますか?
恐らくMacを所有している方が多くDockerを使われている方が多いと思います。

自分が現在、所有しているはHPのSPECTRE X360 13、Windows 10 Homeです。Windows 10 Homeなのです。
ラップトップと言っても非力というほどではありませんがVagrantを立ち上げている間、かなりファンが回り気になります。
それに決して速いとは言えません。さらに現時点ではまだWSL2とDocker for WSLは正式リリースされていません。

ではどうするか?
コメント 2020-01-01 215211.png
https://laragon.org/

Laragonです。
Windows向けのAMP環境構築ソフトで
PHP、Node.js、Python、Java、Go、Rubyをサポートし、
ApacheとNginxのどちらを使用するかを切り替えることができます。
では早速、インストールを行っていきましょう。

1. インストール

コメント 2020-01-01 215427.png
https://github.com/leokhoa/laragon/releases

Githubから最新版を落としてきましょう。
環境構築の経験がない、初学者の方はlaragon-full.exeを選んでいきましょう。
以後はこれをベースに進めます。

インストール先は拘りがなければわかりやすいようデフォルトのC:\Laragonにしておきます。
Laragon 2020-01-12 164745.png

VSCode等を使用する際は"Sublime Text & Terminalを追加する"のチェックが外れた状態にしておきます。
Laragon 2020-01-12 164703.png
ちなみにNotepad++、WinSCP、Putty、HeidiSQL Portable等がインストールされるのでこの1回のインストールだけで開発が出来るようになります。

2. 起動

では起動してみましょう。
Laragon 2020-01-12 165644.png
このスクリーンショットでPHP7.4になっているのは自身で後から追加ができるためです。
"ウェブ"を押すとホスト名を"localhost"ポート番号を8080で設定しているので
"http://localhost:8080/"
の形でブラウザが開きます。
"データベース"でHeidiSQL
"ターミナル"でCmderを起動します。
"ルート"は設定されたルートフォルダを表示します。"ウェブ"で表示される場所も同じものになります。
設定は右上にの歯車マークから表示できる設定で行います。

3. バージョンの追加

今回はPHPを例に紹介します。
https://windows.php.net/download/
から使用するものをダウンロードしてきます。
フォルダを解凍後、
C:\laragon\bin\php\へ追加します。
PythonであればC:\laragon\bin\python\のようになります。
Laragon 2020-01-12 170749.png
追加後に使用するバージョンを左上のメニューから選択します。
Laragon 2020-01-12 171603.png
ここから起動時に使用する言語やバージョン等を切り替えることが出来ます。

4.ガンガン使いましょう!

Macには現時点で対応していませんし、万能とまではいかないまでも
軽く使いやすいので空いた時間でサクッと作業を行うのに役立ちます。
OSがWindows 10 Homeで開発環境で悩んでいる方に特にオススメです。

Sonos で Lチカ してみた。

$
0
0

はじめに

電子工作のはじめの一歩と言えば「Lチカ」ですね。
ワイヤレススピーカーシステム Sonos でも「Lチカ」を試すことができます。
Sonos One にはステータスライトがついており、このステータスライトの色や点滅で状態を確認することができます。(白色点灯状態が正常です)

ただ、夜中暗い中でステータスライトが点灯していると眩しいことがあるので、Sonos アプリからステータスライトを OFF にすることができます。
Sonos_App.jpeg

プログラムから LED を操作する

Sonos は APIが公開されていますので、プログラムから LED を操作することが可能です。

  • getLEDState() →現在の LED の状態を取得します
  • setLEDState(desiredState) → LED の状態を変更します

JavaScript (node.js) の node-sonosモジュールを使用してスクリプトで LED を操作してみました。

インストール

node のバージョンを確認します。(nodebrew で node のバージョン管理を行っています。)

$ nodebrew list
v6.14.1
v8.14.0
v9.11.2
v12.14.1

current: v12.14.1
$ node -v
v12.14.1

node-sonos をインストールします。

$ npm install sonos

スクリプトの実行

Sonos の LED のステータスを確認して、On なら Off へ、Off なら On へ変更するスクリプトです。

$ node setLEDState.js 
Got current LED state "Off"
Success LED: Off->On
$ node setLEDState.js 
Got current LED state "On"
Success LED: On->Off
setLEDState.js
constSonos=require('sonos').Sonosconstsonos=newSonos(process.env.SONOS_HOST||'192.168.13.4',process.env.SONOS_PORT||1400)sonos.getLEDState().then(state=>{console.log('Got current LED state %j',state)if(state=="On"){sonos.setLEDState("Off").then(state=>{console.log('Success LED: On->Off')}).catch(err=>{console.log('Error occurred %j',err)})}if(state=="Off"){sonos.setLEDState("On").then(state=>{console.log('Success LED: Off->On')}).catch(err=>{console.log('Error occurred %j',err)})}}).catch(err=>{console.log('Error occurred %j',err)})

gulpでsassとbabel環境構築(nodeインストールから)

$
0
0

gulpセットアップ

自分用メモなので、大まかな手順のみ。
作業詳細はリンクの各記事を参照。
環境構築を一から行ったことがない人向け(自分!)

作業環境

macOS 10.14.6

手順

  1. node.jsインストール

    nodeのバージョン管理をするため、下記記事を参照しインストール

    MacにNode.jsをインストール

  2. gulp-sass設定

  3. gulp-babel設定
    Babel

  4. Autoprefixer設定
    Autoprefixer

参照

絶対つまずかないGulp 4入門(2019年版) インストールとSassを使うまでの手順

gulp3→4の変更点に気をつけよう!

意味のない感想

自分の投稿見返したら、ほぼ一年前にnpmでwebpack環境構築に手をつけてた、、
けど現在実業務ではgulpで色々ごにょごにょしているのでgulp使い方復習&gulp4の書き方覚えようてことでまずはsassとbabelの設定やってみた。

昨年末はphpとDBの基本だけやったので、Laravel・Vueで開発できる環境構築できればな、、というところ。
手を出しすぎると意味わからなくなるので、少しずつ着実にやっていこう。っていう。

複数のファイルをアップロードしてExpressサーバーで保存する

$
0
0

概要

クライアントアプリからExpressサーバーへの複数ファイルのアップロード時にハマったので、
方法を記載します。

環境

開発環境はMacでNodebrewを使っています。

$ nodebrew 
nodebrew 1.0.1
$ node -v
v12.14.1

単一ファイルのアップロード

まず単一ファイルのアップロード方法を記載します。

Expressサーバー立ち上げ

express-generatorでExpressサーバーを作成します。

$ npm install -g express-generator

express-generatorをインストールしてexpressコマンドが使えるようになったので
アプリを作成します。

$ express --view=ejs express-app

   create : express-app/
   create : express-app/public/
   create : express-app/public/javascripts/
   create : express-app/public/images/
   create : express-app/public/stylesheets/
   create : express-app/public/stylesheets/style.css
   create : express-app/routes/
   create : express-app/routes/index.js
   create : express-app/routes/users.js
   create : express-app/views/
   create : express-app/views/error.ejs
   create : express-app/views/index.ejs
   create : express-app/app.js
   create : express-app/package.json
   create : express-app/bin/
   create : express-app/bin/www

   change directory:
     $ cd express-app

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=express-app:* npm start

上の手順の通りにサーバーを立ち上げます。

$ cd express-app/
$ npm install
$ npm start

以下のURLにアクセスしてサーバーに接続できることを確認します。
http://localhost:3000

スクリーンショット 2020-01-12 15.52.33.png

アップロードAPI作成

サーバーを立ち上げることができたので、一度停止してアップロードAPIを作成します。
app.jsを以下のように修正します。

app.js
varcreateError=require('http-errors');varexpress=require('express');varpath=require('path');varcookieParser=require('cookie-parser');varlogger=require('morgan');varindexRouter=require('./routes/index');varusersRouter=require('./routes/users');// 追加varuploadRouter=require('./routes/upload');varapp=express();// view engine setupapp.set('views',path.join(__dirname,'views'));app.set('view engine','ejs');app.use(logger('dev'));app.use(express.json());app.use(express.urlencoded({extended:false}));app.use(cookieParser());app.use(express.static(path.join(__dirname,'public')));app.use('/',indexRouter);app.use('/users',usersRouter);// 追加app.use('/upload',uploadRouter);

FormDataを処理するためにmulterを追加します。
https://github.com/expressjs/multer

$ npm install multer

routesフォルダ内にupload.jsを作成します。
destで指定したフォルダ内にアップロードされたファイルが保存されます。

upload.js
varexpress=require('express');varmulter=require('multer');varupload=multer({dest:'uploads/'});varrouter=express.Router();router.post('/',upload.single('file'),function(req,res,next){console.log(req.file);console.log(req.body);res.send('upload success');});module.exports=router;

これでAPIが作成できたのでnpm startでサーバーを立ち上げておきます。

アップロードフォーム作成

クライアントアプリ側に以下のようなファイルを作成してアップロードフォームを用意します。
Ajaxを使ってファイルと一緒に適当なパラメータも送信しています。

upload.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8"><title>Uploader</title><script
  src="https://code.jquery.com/jquery-3.4.1.min.js"integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="crossorigin="anonymous"></script></head><body><div><inputtype="file"id="upload"><inputtype="button"id="uploadButton"value="送信"></div><script>$(function(){$('#uploadButton').click(function(){constfiles=$('#upload')[0].files;constformData=newFormData();formData.append('file',files[0]);formData.append('hoge',123);$.ajax({url:'http://localhost:3000/upload',method:'post',data:formData,processData:false,contentType:false}).done(function(res){console.log(res);}).fail(function(err){console.log(err);})})});</script></body></html>

ファイルアップロード

Expressサーバーを立ち上げている状態で上で作成したupload.htmlをブラウザで開いて、
ファイルを選択後に送信ボタンを押下します。

Expressのコンソールに以下のような値が出力されています。
req.fileにアップロードしたファイルの情報、req.bodyにパラメータが格納されています。

{
  fieldname: 'file',
  originalname: 'upload_file.txt',
  encoding: '7bit',
  mimetype: 'text/plain',
  destination: 'uploads/',
  filename: '12dee747383a844dd7d1888578cf720e',
  path: 'uploads/12dee747383a844dd7d1888578cf720e',
  size: 247379
}
[Object: null prototype] { hoge: '123' }
POST /upload 200 5.874 ms - 14

multerのdestにuploadsを指定したので、Expressアプリのルートディレクトリにuploadsフォルダが作成されて
その中にアップロードしたファイルが保存されています。

もし送信ボタン押下時にクロスドメインのエラーが発生する場合は、
以下のようにExpressサーバーにCORSの許可設定を行なってください。

CORS対応

ファイルアップロード時にクロスドメインエラーが発生した場合はExpressにCORSの許可を設定する必要があります。
方法はいくつかありますが、今回はcorsモジュールを使用します。

$ npm install cors

上のコマンドを実行後にapp.jsに以下を追記してください。
今回は全リクエストを許可としていますが、本番運用などする際は適切に設定を行なってください。

app.js
varcreateError=require('http-errors');varexpress=require('express');varpath=require('path');varcookieParser=require('cookie-parser');varlogger=require('morgan');// 追加varcors=require('cors');varindexRouter=require('./routes/index');varusersRouter=require('./routes/users');varuploadRouter=require('./routes/upload');varapp=express();// 追加app.use(cors());

この状態でサーバーを立ち上げてファイルアップロードを行うとクロスドメインエラーが発生しないようになっています。

複数ファイルのアップロード

前置きが長くなりましたが、次に複数ファイルのアップロードを行います。

アップロードAPI作成

まずExpressに複数ファイルのアップロードAPIを作成します。
比較用に単一ファイル用のAPIも残しています。

upload.js
varexpress=require('express');varmulter=require('multer');varupload=multer({dest:'uploads/'});varrouter=express.Router();// 単一ファイルアップロードrouter.post('/',upload.single('file'),function(req,res,next){console.log(req.file);console.log(req.body);res.send('upload success');});// 追加// 複数ファイルアップロードrouter.post('/multiple',upload.array('files'),function(req,res,next){console.log(req.files);console.log(req.body);res.send('multiple upload success');});module.exports=router;

アップロードフォーム作成

クライアント側にも複数ファイルのアップロードフォームを追加します。
ポイントは複数ファイルをFormDataに追加する際に'files'を指定する部分です。

upload.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8"><title>Uploader</title><script
  src="https://code.jquery.com/jquery-3.4.1.min.js"integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="crossorigin="anonymous"></script></head><body><!-- 単一ファイルアップロードフォーム --><div><inputtype="file"id="upload"><inputtype="button"id="uploadButton"value="送信"></div><!-- 追加 --><!-- 複数ファイルアップロードフォーム --><div><inputtype="file"id="multipleUpload"multiple><inputtype="button"id="multipleUploadButton"value="送信"></div><script>$(function(){// 単一ファイルアップロード$('#uploadButton').click(function(){constfiles=$('#upload')[0].files;constformData=newFormData();formData.append('file',files[0]);formData.append('hoge',123);$.ajax({url:'http://localhost:3000/upload',method:'post',data:formData,processData:false,contentType:false}).done(function(res){console.log(res);}).fail(function(err){console.log(err);})})// 追加// 複数ファイルアップロード$('#multipleUploadButton').click(function(){constfiles=$('#multipleUpload')[0].files;constformData=newFormData();for(leti=0;i<files.length;i++){formData.append('files',files[i]);}formData.append('hoge',123);$.ajax({url:'http://localhost:3000/upload/multiple',method:'post',data:formData,processData:false,contentType:false}).done(function(res){console.log(res);}).fail(function(err){console.log(err);})})});</script></body></html>

ファイルアップロード

Expressサーバーを立ち上げている状態で、先ほどと同じようにブラウザから複数ファイルを選択してアップロードを行います。
Expressのコンソールでファイルの情報が出力されて、uploadsフォルダにファイルが保存されていることが確認できます。

[
  {
    fieldname: 'files',
    originalname: 'upload_file 2.txt',
    encoding: '7bit',
    mimetype: 'text/plain',
    destination: 'uploads/',
    filename: '2c8436e8d77723dfaf7a75e38fe1785c',
    path: 'uploads/2c8436e8d77723dfaf7a75e38fe1785c',
    size: 247379
  },
  {
    fieldname: 'files',
    originalname: 'upload_file.txt',
    encoding: '7bit',
    mimetype: 'text/plain',
    destination: 'uploads/',
    filename: '5ed6ed7671d65f269dfaa2c456b2b95b',
    path: 'uploads/5ed6ed7671d65f269dfaa2c456b2b95b',
    size: 247379
  }
]
[Object: null prototype] { hoge: '123' }
POST /upload/multiple 200 19.628 ms - 23

まとめ

単一ファイルのアップロードは割と簡単に実装できましたが、
複数ファイルのアップロードでのクライアントからのFormDataへの追加と
サーバーでの保存がなかなかうまくいかずハマってしまいました。

Formタグを使えばもう少し簡単に実装できたかもしれませんが、
今回は使用せずに実装したかったためこのような方法になりました。

クラウドストレージ「Box」にAPIを使ってファイルをアップロードする

$
0
0

概要

クラウドストレージのBoxにAPI経由でファイルのアップロードを行う必要があったのですが、
調べてもあまり情報がなかったので記載しておきます。

環境

開発環境はMacでNodebrewを使っています。

$ nodebrew 
nodebrew 1.0.1
$ node -v
v12.14.1

カスタムアプリの作成と承認

カスタムアプリの作成

BoxAPIを使うためにまずカスタムアプリを作成します。

デベロッパーコンソールにアクセスしてBoxアカウントにログインします。
https://app.box.com/developers/console

アプリの新規作成を押下します。

アプリの種類でカスタムアプリを選択します。

スクリーンショット 2020-01-13 1.13.16.png

認証方法でJWTを使用したOAuth 2.0 (サーバー認証)を選択します。

スクリーンショット 2020-01-13 1.13.30.png

アプリの名前を入力してアプリの作成を押下します。

スクリーンショット 2020-01-13 1.13.55.png

アプリを作成するとデベロッパーコンソールのマイアプリに追加されるので、
作成したアプリを選択して左のメニューから構成画面を表示します。

スクリーンショット 2020-01-13 1.15.31.png

ここに表示されているDeveloperトークンを使ってAPIを実行することもできますが、
今回は認証キーを使用します。

構成画面内に公開キーの追加と管理という項目があるので、そちらから公開/秘密キーペアを生成します。
※アカウントに二段階認証が設定されている必要があります。

スクリーンショット 2020-01-13 1.16.50.png

キーペアを生成すると認証情報が記載されたJSONファイルが保存されるので、こちらを使って認証を行います。

カスタムアプリの承認

次にBoxアカウントにカスタムアプリの承認を行います。

Boxアカウントの管理コンソールからEnterprise設定を開いて
アプリ→カスタムアプリケーション→新しいアプリケーションを承認を押下します。

スクリーンショット 2020-01-13 1.19.50.png

APIキーの入力画面が表示されるので、
先ほど保存したJSONファイルの中のclientIDを入力して承認します。

スクリーンショット 2020-01-13 1.22.28.png

これでカスタムアプリからAPIを実行する準備ができました。

BoxSDKからAPIの実行

フォルダ内の項目一覧の取得

BoxAPIにはPythonやJAVAのSDKも用意されていますが、今回はbox-node-sdkを使って実行してみます。
https://github.com/box/box-node-sdk

APIリファレンス
https://developer.box.com/jp/reference/

まず適当なフォルダを作成してbox-node-sdkをインストールします。

$ mkdir box-app
$ cd box-app/
$ npm init -y
$ npm install box-node-sdk

作成したフォルダ内に認証情報のJSONファイルを保存しておき、
以下のようなスクリプトを作成します。

こちらの処理では指定したフォルダID内の項目の一覧を取得するAPIを実行しています。
フォルダID'0'はルートディレクトリのIDです。
https://developer.box.com/jp/reference/get-folders-id-items/

script.js
constBoxSDK=require('box-node-sdk');// 認証キーの読み込みconstsdkConfig=require('./config.json');constsdk=BoxSDK.getPreconfiguredInstance(sdkConfig);constclient=sdk.getAppAuthClient('enterprise');// フォルダ内の項目の取得client.folders.getItems('0').then(folder=>{console.log(folder);}).catch(err=>{console.log(err);})

APIを実行してみます。

$ node script.js 
{
  total_count: 0,
  entries: [],
  offset: 0,
  limit: 100,
  order: [
    { by: 'type', direction: 'ASC' },
    { by: 'name', direction: 'ASC' }
  ]

実行するとAPIで取得した項目一覧が出力されますが、
もし自分のBoxアカウントのルートディレクトリにファイルやフォルダを保存していたとしても
ここではなにも表示されないと思います。

ここで取得しているルートディレクトリというのはカスタムアプリ(というアカウント)のルートディレクトリとなるため、
自分のBoxアカウントのルートディレクトリとは別の場所になります。

これだとBoxSDKを使っても結果がわかりづらいのでBoxアカウントとの共有フォルダを作成します。

共有フォルダの作成

まずブラウザで自分のBoxアカウントにアクセスして適当なフォルダを作成します。

管理コンソールからユーザーとグループを開いて
グループ→作成を押下します。

スクリーンショット 2020-01-13 1.06.02.png

グループ名を入力して権限設定でグループメンバーを選択します。

スクリーンショット 2020-01-13 1.28.00.png

メンバーを追加を押下してユーザーの入力欄にカスタムアプリ名を入力すると
サジェストが表示されるのでそちらを選択して追加します。

スクリーンショット 2020-01-13 1.28.52.png

最後にフォルダを共有を押下して先ほど作成したフォルダを選択して完了します。

スクリーンショット 2020-01-13 1.30.07.png

これでカスタムアプリとフォルダの共有を行なっているグループが作成されました。

スクリーンショット 2020-01-13 1.31.23.png

もう一度APIを実行してみると今度は共有フォルダが取得されていることが確認できます。

$ node script.js 
{
  total_count: 1,
  entries: [
    {
      type: 'folder',
      id: '99896711320',
      sequence_id: '0',
      etag: '0',
      name: 'shared'
    }
  ],
  offset: 0,
  limit: 100,
  order: [
    { by: 'type', direction: 'ASC' },
    { by: 'name', direction: 'ASC' }
  ]
}

ファイルアップロード

共有フォルダが作成できたのでファイルアップロードAPIを実行します。
https://developer.box.com/jp/reference/post-files-content/

ソースを以下のように修正します。

script.js
constBoxSDK=require('box-node-sdk');// 追加constfs=require('fs');// 認証キーの読み込みconstsdkConfig=require('./config.json');constsdk=BoxSDK.getPreconfiguredInstance(sdkConfig);constclient=sdk.getAppAuthClient('enterprise');// フォルダ内の項目の取得client.folders.getItems('0').then(folder=>{// 追加fs.readFile('./upload_file.txt',(err,data)=>{if(err){console.log(err);return;}// ファイルアップロードclient.files.uploadFile(folder.entries[0].id,'upload_file.txt',data).then(file=>{console.log(file);}).catch(err=>{console.log(err);})})}).catch(err=>{console.log(err);})

スクリプトを実行するとアップロードしたファイルの情報が出力されます。

$ node script.js 
{
  total_count: 1,
  entries: [
    {
      type: 'file',
      id: '595943878038',
      file_version: [Object],
      sequence_id: '0',
      etag: '0',
      sha1: '',
      name: 'upload_file.txt',
      description: '',
      size: 247379,
      path_collection: [Object],
      created_at: '2020-01-12T08:54:09-08:00',
      modified_at: '2020-01-12T08:54:09-08:00',
      trashed_at: null,
      purged_at: null,
      content_created_at: '2020-01-12T08:54:09-08:00',
      content_modified_at: '2020-01-12T08:54:09-08:00',
      created_by: [Object],
      modified_by: [Object],
      owned_by: [Object],
      shared_link: null,
      parent: [Object],
      item_status: 'active'
    }
  ]
}

Boxアカウントでフォルダを確認するとファイルが保存されています。

スクリーンショット 2020-01-13 1.56.28.png

※フォルダ内に同名ファイルがある状態でアップロードを行うと409エラーが返ってくるので
その場合はファイル名を変更するかファイルのバージョンアップAPIを実行してください。
https://developer.box.com/jp/reference/post-files-id-content/

大容量ファイルのアップロード

サイズの小さいファイルは上の方法でアップロードを行えば良いのですが、
50MBを超えるファイルは分割アップロードを使用することが推奨されているとリファレンスに記載があります。

分割アップロードAPI
https://developer.box.com/jp/reference/get-files-upload-sessions-id/

ただ、APIリファレンスのやり方だと分割アップロードのセッションの作成、
パーツごとに分割アップロード、アップロードセッションのコミットなど複数のAPIを実行する必要があり少し面倒なのですが、
box-node-sdkのドキュメントを見てみるとこれらをまとめて行なってくれる分割アップロード用のAPIが用意されているようです。
https://github.com/box/box-node-sdk/blob/master/docs/files.md#chunked-upload

こちらを使って分割アップロードを試してみます。

サイズが大きいファイルを用意してソースを修正します。
※20MB以下のファイルを分割アップロードするとエラーになります。

script.js
constBoxSDK=require('box-node-sdk');constfs=require('fs');// 認証キーの読み込みconstsdkConfig=require('./config.json');constsdk=BoxSDK.getPreconfiguredInstance(sdkConfig);constclient=sdk.getAppAuthClient('enterprise');// フォルダ内の項目の取得client.folders.getItems('0').then(folder=>{// 修正fs.readFile('./large_file.csv',(err,data)=>{if(err){console.log(err);return;}// 分割ファイルアップロードclient.files.getChunkedUploader(folder.entries[0].id,data.length,'large_file.csv',data).then(uploader=>uploader.start()).then(file=>{console.log(file);}).catch(err=>{console.log(err);})})}).catch(err=>{console.log(err);})

スクリプトを実行するとアップロードしたファイルの情報が出力されます。

$ node script.js 
{
  total_count: 1,
  entries: [
    {
      type: 'file',
      id: '595967596582',
      file_version: [Object],
      sequence_id: '0',
      etag: '0',
      sha1: '',
      name: 'large_file.csv',
      description: '',
      size: 20243278,
      path_collection: [Object],
      created_at: '2020-01-12T09:31:27-08:00',
      modified_at: '2020-01-12T09:31:27-08:00',
      trashed_at: null,
      purged_at: null,
      content_created_at: '2020-01-12T09:31:27-08:00',
      content_modified_at: '2020-01-12T09:31:27-08:00',
      created_by: [Object],
      modified_by: [Object],
      owned_by: [Object],
      shared_link: null,
      parent: [Object],
      item_status: 'active'
    }
  ]
}

Boxアカウントで確認すると大容量ファイルも保存されていることが確認できます。

おわりに

今回BoxAPIをいくつか試していたのですが、検索APIでファイルやフォルダの検索を行うと
作成してから少し時間が経ったものでないと検索に引っかかりませんでした。

もし原因を知っている方がいれば教えていただけると助かります。

Viewing all 9044 articles
Browse latest View live