1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Node.js】Expoプッシュ通知を一斉送信するWebAPI|配列を任意の数に分割をする

Last updated at Posted at 2020-08-12

はじめに

現在Expo ReactNativeを使ってネイティブアプリ開発をしています。
Expoを使ってプッシュ通知を送るためにプッシュ通知のTokenを1つ、1つPOSTリクエストを飛ばしてもいいのですが、Expoの仕様では最大100個のTokenをまとめてPOSTできる。そこでN個ずつに配列を分割して二次元配列にして処理する方法を紹介します。

以下のような長さ75の配列があるとする、それを10分割して余った分は10個未満になっても分割をする。
今回は長さ75の配列を10個づつ分割して二次元配列にする。

処理前

スクリーンショット 2020-08-12 午後6.59.47.png

処理後

スクリーンショット 2020-08-12 午後7.02.02.png

コード

const main = () =>{
  //TOKENにみたて複数の数字を配列にセット
  let arr = []
  for (let i = 0; i < 75; i++) {
    arr.push(i)
  }

  console.log(arr)

  let result_arr = []

  //分割数
  const division_count = 10

  //配列の数を分割数で割った値を切り上げた値
  const add_arr_count = Math.ceil(arr.length / division_count)

  
  //add_arr_countの分だけ空配列を追加する
  for (let i = 0; i < add_arr_count; i++) {
    result_arr.push([])
  }
  
  
  //いい感じに分割して配列にいれる
  arr.forEach((item,index)=>{
    result_arr[Math.floor(index/division_count)].push(item)
  })

  console.log(result_arr)
}


(async () => {
  main()
})()

Expoを使って実際に通知を送る

私の環境ではNode.jsを使ってWebAPIを作り、まとめて送信できるようにしています。
また、Token一覧を管理しているのはMySQLを使っています。

Request body
プッシュ通知のタイトルを設定するpush_titleとプッシュ通知の本文部分にあたるpush_body載せてください

※本物のコードは外部から実行できないようにセキュリティ対策をしています

コード

const express = require('express') // expressモジュールを読み込む
const cors = require('cors') //クロスドメインでアクセスを許可する系のやつ
const bodyParser = require('body-parser') //いい感じにGET POSTを解釈するやつ
const util = require('util') // SQL Async/Await
const axios = require('axios')//npm install axios
const mysql = require('mysql')
const multer = require('multer') // multerモジュールを読み込む これがないとBODYの中身をうまく読み取らない

//↑ 各種、「npm install」してください
//↑ 各種、「npm install」してください
//↑ 各種、「npm install」してください
// 例1 npm i express
// 例2 npm i cors

const app = express() // expressアプリを生成する

app.use(bodyParser())
app.use(express.static('web')) // webフォルダの中身を公開する
app.use(cors())//CROS許可
app.use(multer().none()) // multerでブラウザから送信されたデータを解釈する

//サーバの受付ポート番号、SQL接続情報、設定ファイル読み込み
const config = require('./server_config.json')

// config.server.portのポートでサーバを立てる
app.listen(config.server.port, () => console.log('Listening on port ' + config.server.port))

//MySQL接続情報Async/Await★★★★★★★★★★★★★★★★★★★★★★★★★★★★
//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
const pool_config = {
  host: config.sql.host,
  user: config.sql.user,
  password: config.sql.password,
  port: config.sql.port,
  database: config.sql.database,
  timezone: config.sql.timezone //timezoneの指定省略の場合はシステムローカルになる
}
//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

//汎用的にMySQLを発行する
const runQuery = async (sql, data) => {
  console.log('runQuery...')
  console.log('sql: ' + sql)
  console.log('data: ' + data)

  const pool = mysql.createPool(pool_config)
  pool.query = util.promisify(pool.query)//これないと動かない

  const run_sql = mysql.format(sql, data)
  console.log('発行されたquery:\n' + run_sql + '\n')

  const result = await pool.query(run_sql)
  // console.log(JSON.stringify(result))
  pool.end() // mysqlのコネクションのプロセスを終了させる。
  return result
}

const uuid = require('node-uuid')

app.post('/api/v1/expoPushNotice', (req, res, next) => {
  (async () => {
    console.log('/api/v1/expoPushNotice...')
    
    //私の環境ではMySQLでpush_tokenというTableを作ってそこでTokenを保存しています
    let push_token_list = await runQuery(
      'select `push_token` from `push_token`',
      []
    )

    //通知を送るtoken
    let to_tokens = []
    //分割数
    const division_count = 100
    //配列の追加数 切り上げた値の数だけ配列を追加
    const add_arr_count = Math.ceil(push_token_list.length / division_count)

    console.log('add_arr_countの文だけから配列を追加する')
    //add_arr_countの文だけから配列を追加する
    for (let i = 0; i < add_arr_count; i++) {
      to_tokens.push([])
    }

    push_token_list.forEach((item, index) => {
      to_tokens[Math.floor(index/division_count)].push(item.push_token)
    })

    console.log('to_tokens: ')
    console.log(to_tokens)

    //APIコール
    console.log('pushSend...')
    const req_url = 'https://exp.host/--/api/v2/push/send'

    const headers = {
      headers: {
        Accept: 'application/json',
        'Accept-encoding': 'gzip, deflate',
        'Content-Type': 'application/json',
      },
    }
    //分割した分で別々にPOSTリクエストを飛ばす
    for (let i = 0; i < to_tokens.length; i++) {
      
      //bodyに好きなデータを載せると、アプリ側でいろいろできます。
      const result = await axios.post(
        req_url,
        {
          'to': to_tokens[i],
          'sound': 'default',
          'title': req.body.push_title,
          'body': req.body.push_body,
          'data': {
            
          },
        },
        headers
      )

      console.log('result: ')
      console.log(result)
    }


    res.json({
      status: 200, //
      message: 'リクエストは正常に受信されました',
    })

  })(res, next).catch((e) => {
    console.log('サーバエラー :' + e)
    console.log('res: ' + res)

    res.json({
      status: 500,
      message: 'サーバエラー: ' + e,
    })
  })
})

Tokenを保存しているDB情報

CREATE TABLE `push_token` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `push_token` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `push_token` (`push_token`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

サーバの設定ファイルを記述しているJSONファイル

{
  "sql": {
    "host": "localhost",
    "user": "hoge",
    "password": "hoge",
    "port": 8889,
    "database": "fuga",
    "timezone": "jst"
  },
  "server":{
    "port": 3003
  }
}

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?