m_shige1979のささやかな抵抗と欲望の日々

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

なんとなく作ったサイト

http://www.it-check-matome.info/


Github(注意すること)

https://github.com/mshige1979

nodejsでsocket.ioのサンプルを作成

socket.io

nodejsのモジュールで双方向通信を可能とするモジュール。
WebScoketなどを用いてチャットなどを実行できる。

いままではhttp併用

だったけど、socket.ioのみのバージョンを用意できるようになったらしい

環境

nodejs v8系
※nodebrewなどを使用してバージョンを更新

実装

サーバ側
var io = require('socket.io').listen(3000);

// 接続
io.sockets.on("connect", function(socket) {
    console.log("connected");

    // 接続後にクライアントへ送信する
    socket.emit("message", "接続OKです。");

    // 切断
    socket.on("disconnect", function(data) {
        console.log(data);
        console.log("disconnected");
    });

});
クライアント側
var client = require("socket.io-client");
var socket = client.connect("http://localhost:3000");

// 接続
socket.on("connect", function() {
    console.log("client connected");
});

// メッセージ受信
socket.on("message", function(data) {
    console.log("受信文字:" + data);
});

// 3秒後に切断
setTimeout(function() {
    socket.disconnect();
}, 3000);

今回はここまで

所感

nodejs自体は嫌いではなかったけどなんとなるやる気が行いものでした。
socket.ioをやってみることの必要性が感じられたのでちょっとだけやっている状態。
最近ではスタンドアローンでのサーバークライアントの処理がかなり簡単にかけるようになったのでやっている。
クライアントから任意のタイミングで切断もできるようなので処理も任意に制御できそうかも…

AzurよりSendGridを使用してメールを送信する

Azureでのメール送信はSendGridらしい

詳しくは知らないけどSendGridの送信手段を調べてみました。

環境

CentOS7
PHP7

Azure設定

サービスを追加

f:id:m_shige1979:20170720165411p:plain

アプリ名やパスワードを設定

f:id:m_shige1979:20170720165420p:plain

料金プランを設定

f:id:m_shige1979:20170720165429p:plain

アクセス情報を設定

f:id:m_shige1979:20170720165440p:plain

同意確認を行う

f:id:m_shige1979:20170720165449p:plain

作成ボタンを押下

f:id:m_shige1979:20170720165458p:plain

ダッシュボードに作成されたことを確認

f:id:m_shige1979:20170720165507p:plain

Azureの管理画面より「Manage」を押下してSendGridの管理ページへジャンプ

f:id:m_shige1979:20170720165518p:plain

メールを送信して認証チェックを行う

f:id:m_shige1979:20170720165527p:plain

メール送信完了画面

f:id:m_shige1979:20170720165535p:plain

自分のメールクライアントよりメールを受信してボタンを押下

f:id:m_shige1979:20170720165545p:plain

認証後はそのまま管理画面を表示

f:id:m_shige1979:20170720165648p:plain

APIキー作成画面を表示

f:id:m_shige1979:20170720165657p:plain

作成ボタンを押下して作成画面を表示

f:id:m_shige1979:20170720165708p:plain

APIキー名と権限を設定

f:id:m_shige1979:20170720165719p:plain

キーの値をクリックしてコピーする

f:id:m_shige1979:20170720165729p:plain

Azureの設定画面で資格情報を取得

f:id:m_shige1979:20170720165739p:plain
※パスワードは設定したもの、ユーザ名はこれを使用することでメール送信のパラメータとなります。

送信用設定

yum
udo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
sudo yum install --enablerepo=remi,remi-php70 php php-devel php-mbstring php-pdo php-gd php-xml php-intl
composer
mkdir -p sample1
cd sample1
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
php composer.phar require swiftmailer/swiftmailer
composer.json
{
    "require": {
        "swiftmailer/swiftmailer": "5.3.0"
    }
}

実装

sendmail1.php
<?php
include_once "vendor/autoload.php";

// 本文
$text = "Hi!\nHow are you?\n";

// 送信元
$from = array(
    '送信元メールアドレス' => 'sample_from'
);

// 送信先
$to = array(
    '送信先メールアドレス' => 'sample_to',
);

// タイトル
$subject = 'Example PHP Email';

// ユーザ情報
$username = '資格情報のユーザ名';
$password = '資格情報のパスワード';

// 送信設定
$transport = Swift_SmtpTransport::newInstance('smtp.sendgrid.net', 587);
$transport->setUsername($username);
$transport->setPassword($password);
$swift = Swift_Mailer::newInstance($transport);

// メッセージ生成
$message = new Swift_Message($subject);

// 送信情報設定
$message->setFrom($from);
$message->setBody($text, 'text/plain');
$message->setTo($to);

// 送信処理
if ($recipients = $swift->send($message, $failures)) {
   echo "送信成功";
} else {
    echo "送信失敗";
}
sendmail2.php
<?php

// 接続情報
$url = 'https://api.sendgrid.com/';
$user = '資格情報より取得したユーザ名';
$pass = '資格情報より取得したパスワード';

// 送信情報
$params = array(
    'api_user' => $user,
    'api_key' => $pass,
    'to' => '送信先のメールアドレス',
    'subject' => 'testing from curl',
    'text' => 'testing body',
    'from' => '送信元のメールアドレス',
);

// リクエスト先設定
$request = $url.'api/mail.send.json';

// curl初期化
$session = curl_init($request);

// curl設定(POST送信)
curl_setopt ($session, CURLOPT_POST, true);

// curl設定(パラメータ設定)
curl_setopt ($session, CURLOPT_POSTFIELDS, $params);

// curl設定(ヘッダー不要、レスポンス必要?)
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

// 送信して終了
$response = curl_exec($session);
curl_close($session);

// 処理結果
print_r ($response);

※WEB APIを使用してメール送信するサンプルでもユーザとパスワードは資格情報のものを使用します。

所感

参考にしたドキュメントの通りにやることでなんとかできた。
最近は日本語のドキュメントが豊富なので環境を用意するのも簡単で助かります。
一部英語でわからないこともあるけど
まあなんとかなるかな…

Azure App FunctionsでLINE Botへプッシュ送信してみる

LINE Botにプッシュ送信はしたことあるけど

AWSのラムダとかはあまり使ったことがないので動かせるかはわからないのでやってみる

準備

LINEの開発アカウントは取得しておく
LINE Business Center

App Functionsを用意

前回のものを利用

npmインストー

Kuduを起動する

f:id:m_shige1979:20170717230744p:plain

CMDを選択する

f:id:m_shige1979:20170717230754p:plain

コマンドを確認する

f:id:m_shige1979:20170717230804p:plain

jsアプリの場所まで移動し、npm installを実施

f:id:m_shige1979:20170717230815p:plain

スクリプト修正

index.js
module.exports = function (context, myBlob) {
    // Blob情報
    var msg = "blobInfo " + "\n" +
        "Name:" + context.bindingData.name + "\n" +
        "Size:" + myBlob.length + "Bytes" + "\n";

    var accessToken = "チャネルアクセストークン";

    var request = require('request');
    var options = {
        uri: "https://api.line.me/v2/bot/message/push",
        method: "POST",
        headers: {
            "Content-type": "application/json",
            "Authorization": "Bearer " + accessToken
        },
        json: {
            "to": "テスト用のプッシュID",
            "messages":[{
                "type": "text",
                "text": msg
            }]
        }
    };
    request.post(options, function(error, response, body){
        context.log("error");
    });
    
    context.done();
};

実行

f:id:m_shige1979:20170717231107p:plain

所感

なんかちょっと遅く感じるような…
設定などに応じて時間が少しかかっているのかもしれない
まあ、動作することは確認できたのでOKとする

Azure App Functionsをやってみる

これは?

簡単にいうとAWSのラムダと同じでなんらかのトリガーで動くスクリプトサービスのようです。

今回やること

Blobへアップロードすることは前回やったのでこれをトリガーとしてコンソールにログを出力するだけのサンプルを作成する。

Azure

新規作成

f:id:m_shige1979:20170717214128p:plain

Function Appを選択

f:id:m_shige1979:20170717214138p:plain

作成を選択

f:id:m_shige1979:20170717214148p:plain

アプリ名と他の設定を行い、作成する

f:id:m_shige1979:20170717214158p:plain

ダッシュボードで作成されていることを確認

f:id:m_shige1979:20170717214208p:plain

関数を追加

f:id:m_shige1979:20170717214218p:plain

カスタム関数を作成する

f:id:m_shige1979:20170717214232p:plain

BlobTrigger(Javascript)を選択する

f:id:m_shige1979:20170717214243p:plain

作成を選択する

f:id:m_shige1979:20170717214255p:plain

Blobのストレージへファイルをアップロードする

f:id:m_shige1979:20170717214305p:plain

スクリプトが動いていることを確認

f:id:m_shige1979:20170717214315p:plain

今回はここまで

所感

簡単な起動は確認できた、他のWebAPIへ投げたりできるか確認して見る。

Azure Strage File ApiをPHPでBLOBを取得

仕事がやっと

落ち着いた感じがする。
特別忙しいというわけではないがタイミングなどの問題もあってブログを書くことを忘れてしまっているのはマズイ
忘れていることもあるので勉強を再開してみる。

Azure Strage File

簡単にいうとAWSのS3と思う、厳密には違うと思うけど一部のデータを保存したり取得したりするのでそのレベルの認識で覚えておく
今回は設定して登録したデータをphpで取得することを行う。

事前準備

Windows Azureへアカウントを作成しておく
Linuxの環境(vagrantなどを作成しておく)

Azureの設定

ストレージアカウントを選択する

f:id:m_shige1979:20170717081218p:plain

追加ボタンを押下

f:id:m_shige1979:20170717081231p:plain

アカウント名と他の設定を行い、作成ボタンを押下

f:id:m_shige1979:20170717081242p:plain
f:id:m_shige1979:20170717081254p:plain

作成されたことを確認し、選択する

f:id:m_shige1979:20170717081305p:plain

コンテナを追加する

f:id:m_shige1979:20170717081318p:plain

コンテナ名を設定してOKを押下

f:id:m_shige1979:20170717081330p:plain

アクセスキーをコピーする

f:id:m_shige1979:20170717081341p:plain

準備

php
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
sudo yum install --enablerepo=remi,remi-php70 php php-devel php-mbstring php-pdo php-gd php-xml
compoer
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
compoer.json
{
  "require": {
    "microsoft/windowsazure": "^0.4"
  }
}

設定情報

setting.php
<?php

$connectionString = "DefaultEndpointsProtocol=https;AccountName=mshige1979sample1;AccountKey=事前にコピーしたアクセスキー";

ファイルを追加

sample_add.php
<?php
require_once 'vendor/autoload.php';
require_once 'setting.php';

use WindowsAzure\Common\ServicesBuilder;
use MicrosoftAzure\Storage\Common\ServiceException;

// 接続文字列を使用して、Blobサービスのインスタンスを作成する
$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($connectionString);

// 取得処理
try {
    // ファイル名
    $date = "testdata_" . date("YmdHis") . ".txt";
    echo $date . "\n";

    // データの中身
    $data = md5(uniqid());;
    echo $data . "\n";

    // 追加処理
    $blobRestProxy->createBlockBlob("test01", $date, $data);

} catch(ServiceException $e) {
    // 例外処理
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."\n";
}

$ php sample_add.php
testdata_20170716233116.txt
08a8e1964d2c0dadc558bd4f54b3de5a
$ php sample_add.php
testdata_20170716233127.txt
6ff9937a10cbc58bee43367299f49a80
$ 

一覧取得

sample1.php
<?php
require_once 'vendor/autoload.php';
require_once 'setting.php';

use WindowsAzure\Common\ServicesBuilder;
use MicrosoftAzure\Storage\Common\ServiceException;

// 接続文字列を使用して、Blobサービスのインスタンスを作成する
$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($connectionString);

// 取得処理
try {
    // コンテナのオブジェクトを取得
    $blob_list = $blobRestProxy->listBlobs("test01");
    $blobs = $blob_list->getBlobs();

    // 一覧を出力
    foreach ($blobs as $blob) {
        echo $blob->getName().": ".$blob->getUrl()."\n";
    }
} catch(ServiceException $e) {
    // 例外処理
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."\n";
}

$ php sample_list.php
testdata_20170716233116.txt: https://mshige1979sample1.blob.core.windows.net/test01/testdata_20170716233116.txt
testdata_20170716233127.txt: https://mshige1979sample1.blob.core.windows.net/test01/testdata_20170716233127.txt
$

読込処理

sample_get.php
<?php
require_once 'vendor/autoload.php';
require_once 'setting.php';

use WindowsAzure\Common\ServicesBuilder;
use MicrosoftAzure\Storage\Common\ServiceException;

// 接続文字列を使用して、Blobサービスのインスタンスを作成する
$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($connectionString);

// 取得処理
try {
    // ファイル名
    $date = "testdata_20170716233116.txt";

    // 取得処理
    $blob = $blobRestProxy->getBlob("test01", $date);
    echo stream_get_contents($blob->getContentStream()) . "\n";

} catch(ServiceException $e) {
    // 例外処理
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."\n";
}

$ php sample_get.php
08a8e1964d2c0dadc558bd4f54b3de5a
$

まとめ

AWSとAzureはそれぞれ勝手が違う。
このレベルのことは基本として理解しておきたい、やっていないからわかりませんということは仕方がないにしても
ITに携わるものとして新しい技術に触れ続けていくことは重要と思うので…

AWSのSESで送信者名を指定する

メール送信で日本語を使用したい

文字化け起こりましたのでネットを使用して調べました

以下の対応でなんとかなりました。m(_ _)m

実装

import java.io.UnsupportedEncodingException;

import javax.mail.internet.InternetAddress;

import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient;
import com.amazonaws.services.simpleemail.model.Body;
import com.amazonaws.services.simpleemail.model.Content;
import com.amazonaws.services.simpleemail.model.Destination;
import com.amazonaws.services.simpleemail.model.Message;
import com.amazonaws.services.simpleemail.model.SendEmailRequest;

public class Sample01 {
	
	private static final String ACCESS_KEY = "アクセスキー";
	private static final String SECRET_ACCESS_KEY = "シークレットアクセスキー";
	
	public static void main(String[] args) {
		
		String TO = "送信先のメールアドレス";
		String FROM = "送信元のメールアドレス";
		String SUBJECT = "テストメール";
		String BODY = "テストメールです。";
		
		// 宛先
		Destination destination = new Destination().withToAddresses(new String[]{TO});
	    // 件名
	    Content subject = new Content().withData(SUBJECT);
		// 本文
	    Body body = new Body().withText(new Content().withData(BODY));
	    
	    // メッセージ
	    Message message = new Message()
	        .withSubject(subject)
	        .withBody(body);
	    
	    // リクエストを作成
	    SendEmailRequest request = new SendEmailRequest()
	        .withSource(senderAddressBuilder(FROM,"送信者です"))
	        .withDestination(destination)
	        .withMessage(message);

	    // 認証
	    AmazonSimpleEmailServiceClient client = new AmazonSimpleEmailServiceClient(
	    		new  BasicAWSCredentials(ACCESS_KEY, SECRET_ACCESS_KEY));
	    
	    // リージョンを設定
	    client.setRegion(Region.getRegion(Regions.US_WEST_2));
	    
	    // メール送信
	    client.sendEmail(request);
		
	}
	
	private static String senderAddressBuilder(String fromAddress, String senderName) {
	    try {
	        InternetAddress address = new InternetAddress(fromAddress,
	                senderName, "ISO-2022-JP");
	        return address.toString();
	    } catch (UnsupportedEncodingException e) {
	    	System.out.println("Internet Address Constructor throws UnsupportedEncoding");
	    }
	    return fromAddress;
	}

}

文字コードは任意で設定すればいいかも
別に必須ではないかもしれません。
address.toString()である程度はいい感じに対応してくれそうです。

Spring Bootで画面からAPIへRest Templateでファイルを送信する

シンプルな使い方

jsonで送信する

難しそうな使い方

fileを送信する

やりたいこと

画面からsubmitまたはajaxでファイルを送信する。

問題点

処理の流れが
①画面でファイルを指定する
②画面でsubmitする
③Rest Templateへファイルを送信する

この場合、ファイルオブジェクトの情報をRest Templateへ渡す必要がありますが、MultipartFileをそのまま渡すことはできなかった。
別のファイルを出力したりしないといけないのかと思っていました…

Rest Templateのサンプル

Sample
package com.example.web;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import com.example.form.UploadForm;

@Controller
public class UploadController {
    
    private static final Logger logger = LoggerFactory.getLogger(UploadController.class);
    
    @RequestMapping(value = "/upload", method = {RequestMethod.POST})
    public String upload(Model model, @ModelAttribute UploadForm form) throws IOException {
        
        logger.info(form.getName());
        logger.info(form.getUploadFile().getName());
        logger.info(form.getUploadFile().getOriginalFilename());
        logger.info(String.valueOf(form.getUploadFile().getSize()));
        
        RestTemplate restTemplate = new RestTemplate();
        
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
        
        
        ByteArrayResource contentsAsResource = new ByteArrayResource(form.getUploadFile().getBytes()){
            @Override
            public String getFilename(){
                return form.getUploadFile().getOriginalFilename();
            }
        };
        map.add("name", form.getName());
        map.add("uploadFile", contentsAsResource);
        
        HttpHeaders formHeaders = new HttpHeaders();
        formHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        
        HttpEntity<MultiValueMap<String, Object>> formEntity = 
                new HttpEntity<MultiValueMap<String, Object>>(map, formHeaders);
        
        ResponseEntity<String> resposne =
                restTemplate.exchange("http://localhost:8080/api/upload", 
                        HttpMethod.POST, formEntity, String.class);
        
        return "/upload";
    }
    
}

MultipartFileのデータをByteArrayResourceへ再定義したものを用意して送信することでRestTemplateはファイルとして認識して送信してくれる感じ
受信データ自体はよしなに対応すればいい感じかと…

受信側

package com.example.api;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.example.dto.UploadDto;
import com.example.web.UploadController;

@RestController
public class ApiUploadController {
	
	private static final Logger logger = LoggerFactory.getLogger(ApiUploadController.class);
	
	@RequestMapping(value = "/api/upload", method = {RequestMethod.POST})
	public String upload(UploadDto reqDto) throws IOException {
		
		logger.info("/api/upload" + "start");
		
		logger.info(reqDto.getName());
		logger.info(reqDto.getUploadFile().getName());
		logger.info(reqDto.getUploadFile().getOriginalFilename());
		logger.info(new String(reqDto.getUploadFile().getBytes(), "UTF-8"));
		
		logger.info("/api/upload" + "end");

		return "aaaaa";
	}
	
}

Form、DTO

package com.example.form;

import java.io.Serializable;

import org.springframework.web.multipart.MultipartFile;

public class UploadForm implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private MultipartFile uploadFile;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public MultipartFile getUploadFile() {
		return uploadFile;
	}
	public void setUploadFile(MultipartFile uploadFile) {
		this.uploadFile = uploadFile;
	}
}

所感

まあ、いろいろ探してみるもんかと思いました。
最初はできないような気がすると思っていましたがよくよく考えたらそのあたりのことを考慮していないわけがないと思いましたし…
Springは難しいですね(^^)