20210302のNode.jsに関する記事は6件です。

Cookieを使った認証機能を実装しようとしてCORSでどハマりした時のメモ

前提

やりたかったこと

  • サーバーで発行されたcredential情報をcookieとして保存すること
  • cookieに保存されているcredential情報をサーバーに送付して、それを元に認証機能を実装すること

はまったこと

  • クライアントからのリクエストに対して、サーバー側からのレスポンスにSet-Cookieヘッダーを付与することは出来て、ブラウザのdeveloper toolでもSet-Cookieヘッダーがレスポンスにあることを確認出来ているのに、クライアントでcookieがセットされない。

原因

  • 原因はCORSの設定。

解決のために参考したリンク(ほんとまじでありがとうございます!)

await axios.get('apiのエンドポイント', { withCredentials: true });
  import cors from 'cors';
  const app = express();
  app.use(cors({ origin: true, credentials: true }));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsでscss導入しようとしたらエラッた

開発環境

macOS: 10.15.7
node: 14.15.3
npm: 6.14.9

再現方法 (2021.03.02時点)

npx create-nuxt-app projectName-xxxx

上記で諸々をインストール後に、下記でscssに必要なパッケージをインストール

npm install --save-dev node-sass sass-loader @nuxtjs/style-resources

そしてvueファイルの

<style>
  ...
</style>

を、↓に書き換える

<style lang="scss">
  ...
</style>

そして、npm run devを実行すると下記エラー

 ERROR  Failed to compile with 1 errors                                                                                                                                                                                                                                                               friendly-errors 20:33:55


 ERROR  in ./pages/index.vue?vue&type=style&index=0&lang=scss&                                                                                                                                                                                                                                        friendly-errors 20:33:55

Module build failed (from ./node_modules/sass-loader/dist/cjs.js):                                                                                                                                                                                                                                    friendly-errors 20:33:55
TypeError: this.getOptions is not a function
    at Object.loader (/Users/xxxx/node_modules/sass-loader/dist/index.js:25:24)

原因

Nodeと下記パッケージのバージョンがうまく噛み合っていないよう。
この段階でインストールされているパッケージのバージョンは↓

package.json

    "node-sass": "^5.0.0",
    "sass-loader": "^11.0.1",

解決方法

node-sasssass-loaderのバージョンを下げたらエラー解消した

解決方法の詳細

1. まずはnode-sassのバージョンを下げてみる

npm uninstall --save-dev node-sassでアンインストールして、
npm install --save-dev node-sass@4.14でバージョン指定して再インストール

npm run dev 実行 → エラー解消せず(先述のエラー発生)

2. sass-loaderのバージョンも下げてみる

npm uninstall --save-dev sass-loaderでアンインストールして、
npm install --save-dev sass-loader@10.1.0でバージョン指定して再インストール

npm run dev 実行 → エラー解消!!

所感

エラー解消してよかった〜

補足

今回のエラーに関しては、ググった感じでは他に起因するケースもあるようです。
本記事は原因の一つとしてご参考までにmm

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】Expressでセッション機能を使う方法

プログラミング勉強日記

2021年3月2日

セッション機能とは

 Webサーバーとブラウザの間で続けて通信を行うために必要な機能で、サーバー側でクライアントの状態を管理する方法である。セッションを利用することで、同じクライアントからサーバーに何回アクセスされたかなどを管理することができる。

Express sessionの使い方

 express-sessionモジュールをインストールして、--saveオプションを使用することでインストールの情報を保存できる。

$ npm install --save express-session

 そうすると、package.jsonにexpress-sessionが追加される。

package.jsoon
"dependencies": {
    "ejs": "^2.6.1",
    "express": "^4.16.4",
    "express-session": "^1.15.6"
}

sessionの基本構文

 まず、インスタンス名で指定したオブジェクトに対してuseでセッションを使用すると宣言する。sessionでセッション処理を行うことを指定して、値のところで具体的にどのような処理を行うか指定する。

インスタンス名.use(session({
  設定項目: '値',
}))

参考文献

セッション機能について
セッションを扱う!express-sessionを利用する方法【初心者向け】
express-sessionでセッションを利用する[Express][node.js]

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでcsvファイルを読み書きする

はじめに

node.jsでcsvを読み書きしたい時があったので,備忘録としてまとめようと思い,この記事を書きました。
streamを使ったやり方など,色々と方法があったのですが,今回は fs.readFileSyncfs.writeFileSync を用いた方法でやってみました。

大まかな流れ

  1. 必要なパッケージのダウンロード
  2. 読み込むcsvファイルの作成
  3. 実行ファイルの作成
  4. 実行

必要なパッケージのダウンロード

今回は, csv というモジュールを使用しますので,以下コマンドを使用してダウンロードします。

$ npm install --save csv
  • --save : package.jsonのdependenciesに追加される

読み込むcsvファイルの作成

先頭の行に,カラム名を入れておきます。

input.csv
name,age,gender
taro,30,man
jiro,28,man
hanako,20,woman 

実行ファイルの作成

実行ファイルの完成形

input.csv というcsvファイルを読み込み,行を追加して output.csv という名前で出力するコードになっています。

sample.js
// file system モジュールを読み込む
const fs = require('fs');

// csv モジュールの内,必要な機能を読み込む
const parse = require('csv-parse/lib/sync');
const stringify = require('csv-stringify/lib/sync');

// csvファイルを読み込む
const inputData = fs.readFileSync('./input.csv', { encoding : 'utf8' });

// 先頭行をcolumnとして扱い,csvデータをparse
const parsedData = parse(inputData, { columns : true });

// 行を追加
parsedData.push({ 'name': 'saburo', 'age': '25', 'gender': 'man'});

// オブジェクトのプロパティを先頭行に記述し,csvデータに変換
const outputData = stringify(parsedData, { header : true });

// csvファイルを出力
fs.writeFileSync('output.csv', outputData, { encoding: 'utf8' });

以下で詳細に説明します。

モジュールの読み込み

sample.js(一部)
// file system モジュールを読み込む
const fs = require('fs');

// csv モジュールの内,必要な機能を読み込む
const parse = require('csv-parse/lib/sync');
const stringify = require('csv-stringify/lib/sync');

実装に必要なモジュールを読み込みます。(csv-parse と csv-stringfy については,後ほど詳しく説明します。)

csvファイルを読み込む

sample.js(一部)
// csvファイルを読み込む
const inputData = fs.readFileSync('./input.csv', { encoding : 'utf8' });

fsモジュールを用いて, input.csv を読み込みます。
今回は,オプションで文字コードを UTF-8 と指定しています。

csvファイルをオブジェクトに変換

sample.js(一部)
// 先頭行をcolumnとして扱い,csvデータをparse
const parsedData = parse(inputData, { columns : true });

fs.readFileSync でcsvファイルを読み込むと,以下のように単なる文字列として保存されます。

inputDataの中身
name,age,gender
taro,30,man
jiro,28,man
hanako,20,woman

これを,javascriptで操作しやすくするためにオブジェクトの形式に変更するのが, csv-parse です。
columns : true というオプションをつけることによって,csvファイルの先頭行を,オブジェクト変換後のプロパティとして扱います。

parsedDataの中身
[
  { name: 'taro', age: '30', gender: 'man' },
  { name: 'jiro', age: '28', gender: 'man' },
  { name: 'hanako', age: '20', gender: 'woman' }
]

末尾に行を追加

sample.js(一部)
// 行を追加
parsedData.push({ 'name': 'saburo', 'age': '25', 'gender': 'man'});

オブジェクトをcsvデータに変換

sample.js(一部)
// オブジェクトのプロパティを先頭行に記述し,csvデータに変換
const outputData = stringify(parsedData, { header : true });

csv-stringify は, csv-parse とは逆に,javascriptのオブジェクトをcsvファイルとして出力するための文字列に変換するモジュールです。
変換されたデータは,以下のような中身になっています。

outputDataの中身
name,age,gender
taro,30,man
jiro,28,man
hanako,20,woman
saburo,25,man

csvファイルを出力

sample.js(一部)
// csvファイルを出力
fs.writeFileSync('output.csv', outputData, { encoding: 'utf8' });

fs モジュールを用いて, output.csv という名前でcsvファイルを出力します。

実行

$ node sample.js
output.csv
name,age,gender
taro,30,man
jiro,28,man
hanako,20,woman
saburo,25,man

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Express.jsでファイルダウンロード

Express.jsで画像などのファイルをダウンロードする方法です。
本記事の内容は以下のドキュメントに書かれている内容の説明になります。
https://expressjs.com/ja/api.html

実行環境

express.js 4.17.1
MacOS 10.15.7

方法その1 簡易的な方法

import express from 'express';

export const router: express.Router = express.Router()

router.get('/download', (req, res) => {
  res.download('images/どね.jpg')
})

downloadメソッドを使います。ファイルパスを引数に取ることになるので、一度ファイルをストレージのどこかに置く必要があります。すでにBuffer形式になっている場合は少し面倒ですね。

方法その2 headerに指定

import express from 'express';
import fs from 'fs/promises'

export const router: express.Router = express.Router()

router.get('/set', async (req, res) => {
  const img = await fs.readFile('images/どね.jpg')
  const fileName = encodeURIComponent('どね.jpg')
  res.set({'Content-Disposition': `attachment; filename=${fileName}`})
  res.status(200).send(img)
})

レスポンスのヘッダーを直接指定します。Content-Dispositionattachmentを指定することでブラウザ側でダウンロードファイルであることを認識してくれます。
それ以外にも以下の処理が必要になります。

  • Bufferファイルを一度読み込んでbodyに入れる
  • 日本語ファイル名の可能性がある場合はファイル名にエンコードをかける

filenameとfilename*の違いについて

引数の filename と filename* の違いは、 filename* が RFC 5987 で定義されているエンコーディングを使用するという点のみです。単一のヘッダーフィールドの値に filename と filename* の両方が存在する場合は、両方が解釈できる場合、 filename* が filename よりも優先されます。

方法1ではfilename*が自動で使われて、方法2ではどちらも指定できます。

方法1 方法2
image.png image.png

いくつかのブラウザで試しましたがすべてfilename*を認識できていました。使える文字もこちらのほうが多いようです。あえてfilenameを使う必要はなさそうです。もし使えないブラウザがあったら教えていただきたいです。

試したブラウザを以下に置いておきます。

ブラウザ バージョン 対応状況
Chrome 88
FireFox 88
Microsoft Edge 88
Internet Explorer 11

まとめ

たいていはフレームワークに乗っかった方が後々困ることもないので方法1推奨です。
修正がかかったらフレームワーク側を直せば良い話ですし。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScript(Node.js)でテキストファイルを指定行数ごとに分割する

テキストファイルを分割したいときってありますよね。
例えばSQLのINSERT INTO hoge VALUESに続く行が数万行ある時とか (そんなにない)

個人的に上記をやりたいタイミングがあって、npmでいい感じのモジュールを探したんですが
意外とテキストファイルを「指定した行数で」区切ってくれるやつが無かったので泣く泣く自分で作りました。

あ、Nodeです。

Streamで順次処理してるので、でっかいファイルでもヒープアウトしないはず!

Usage

hoge.ts
import { FileSplitter } from "path/to/file-splitter"

// your-file.txtを100行ごとに分割する
const fileSplitter = new FileSplitter("/path/to/your-file.txt", 100)

// 分割開始!
fileSplitter.start()

御託は良いからコードを見せろ

file-splitter.ts
import fs from "fs"
import path from "path"
import readline from "readline"

export class FileSplitter {
  private maxLines: number
  private identifier = 0
  private curLine = 0
  private lineReader: readline.Interface
  private currentWriteStream: fs.WriteStream
  private outputPath: string

  constructor(readFrom: string, maxLines = 500) {
    // 分割したファイル群を保存する先のディレクトリを作り、そのpathを保存
    this.setOutputDir(readFrom)

    this.maxLines = maxLines

    // 最初のWriteStreamを作っておく
    this.replaceWriteStream()

    // readlineに指定ファイルのReadStreamを食わせて行リーダーを作る
    this.lineReader = readline.createInterface({
      input: fs.createReadStream(readFrom)
    })
  }

  start() {
    this.lineReader
      .on("line", (line) => {
        // 現在の行数を保持 (1 ~ 指定行数 + 1までの値を取る)
        this.curLine++

        // ここ、もう少しうまくやりたかった人生だった
        const isOn = this.curLine === this.maxLines
        const isOver = this.curLine > this.maxLines

        if (isOver) {
          // 指定行数を超えたら新しいファイル向けのWriteStreamに切り替え、行数を1にリセットする
          this.replaceWriteStream()
          this.curLine = 1
        }

        if (isOn) {
          // そのファイル最後の行は改行無しにしている (が、例えば100行のファイルを30行とかで区切られると、最後のファイルには改行が入っちゃう。めんどくさくてこれ以上考えなかった)
          this.currentWriteStream.write(line)
        } else {
          this.currentWriteStream.write(`${line}\n`)
        }
      })
      .on("close", () => {
        // 一応掃除する
        this.closeWriteStreamIfExists()

        console.info("Done!")
      })
  }

  private setOutputDir(readFrom: string) {
    const extension = path.extname(readFrom)
    const fileName = path.basename(readFrom, extension)

    const splitFileBaseDir = `${path.dirname(readFrom)}/split-files`

    this.outputPath = `${splitFileBaseDir}/${fileName}`

    if (!fs.existsSync(splitFileBaseDir)) {
      fs.mkdirSync(splitFileBaseDir)
    }

    if (!fs.existsSync(this.outputPath)) {
      fs.mkdirSync(this.outputPath)
    }
  }

  private replaceWriteStream(): void {
    this.identifier++

    this.closeWriteStreamIfExists()

    const writeTo = `${this.outputPath}/file_${this.identifier}.txt`

    console.info(`Start writing to ${writeTo}.\n`)

    this.currentWriteStream = fs
      .createWriteStream(writeTo)
  }

  private closeWriteStreamIfExists() {
    if (this.currentWriteStream) {
      this.currentWriteStream.close()
    }
  }
}

まとめ

readline標準モジュールにたどり着くまでの人生を無駄にした。

Streamをちゃんと考えて使ったことあんまりなかったのでちょっと楽しかった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む