m_shige1979のときどきITブログ

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

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

AWSのS3へJavaのSDKを使用してアップロード

AWSへプログラムよりアクセスする

なんかやることが多くなってきた

準備

AWSのIAMでS3用のユーザーを作成し、アクセスキーとシークレットアクセスキーを取得

eclipseへ機能を追加

新規ソフトウェアのインストールで「https://aws.amazon.com/eclipse」を設定

f:id:m_shige1979:20170108152855p:plain

アクセスキーとシークレットアクセスキーを設定する

f:id:m_shige1979:20170108153553p:plain

設定を変更して東京リージョンへする

f:id:m_shige1979:20170108154216p:plain

eclipse

プロジェクトで「AWS Java Project」で作成

f:id:m_shige1979:20170108155455p:plain

Amazon S3 Sample

f:id:m_shige1979:20170108155645p:plain

サンプル
package com.example.awssample;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.UUID;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;

public class S3Sample {

    public static void main(String[] args) throws IOException {

        // eclipseの設定ファイルのパスよりアクセスキー、シークレットアクセスキーを参照
        AWSCredentials credentials = null;
        try {
            credentials = new ProfileCredentialsProvider("default").getCredentials();
        } catch (Exception e) {
            throw new AmazonClientException(
                    "Cannot load the credentials from the credential profiles file. " +
                    "Please make sure that your credentials file is at the correct " +
                    e);
        }

        AmazonS3 s3 = new AmazonS3Client(credentials);
        Region usWest2 = Region.getRegion(Regions.AP_NORTHEAST_1);
        s3.setRegion(usWest2);

        String bucketName = "awsuploadsample";
        String key = UUID.randomUUID().toString();

        System.out.println("===========================================");
        System.out.println("Getting Started with Amazon S3");
        System.out.println("===========================================\n");

        try {
            // バケット名
            System.out.println("Creating bucket " + bucketName + "\n");
            
            // バケット一覧
            System.out.println("Listing buckets");
            for (Bucket bucket : s3.listBuckets()) {
                System.out.println(" - " + bucket.getName());
            }
            System.out.println();

            // ファイルアップロード
            System.out.println("Uploading a new object to S3 from a file\n");
            s3.putObject(new PutObjectRequest(bucketName, key, createSampleFile()));

            // ・ダウンロード
            System.out.println("Downloading an object");
            S3Object object = s3.getObject(new GetObjectRequest(bucketName, key));
            System.out.println("Content-Type: "  + object.getObjectMetadata().getContentType());
            displayTextInputStream(object.getObjectContent());

            // 一覧を表示
            System.out.println("Listing objects");
            ObjectListing objectListing = s3.listObjects(new ListObjectsRequest()
                    .withBucketName(bucketName)
                    .withPrefix("My"));
            for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
                System.out.println(" - " + objectSummary.getKey() + "  " +
                                   "(size = " + objectSummary.getSize() + ")");
            }
            System.out.println();

            // アップロードファイルの削除
            //System.out.println("Deleting an object\n");
            //s3.deleteObject(bucketName, key);
            
        } catch (AmazonServiceException ase) {
            System.out.println("Caught an AmazonServiceException, which means your request made it "
                    + "to Amazon S3, but was rejected with an error response for some reason.");
            System.out.println("Error Message:    " + ase.getMessage());
            System.out.println("HTTP Status Code: " + ase.getStatusCode());
            System.out.println("AWS Error Code:   " + ase.getErrorCode());
            System.out.println("Error Type:       " + ase.getErrorType());
            System.out.println("Request ID:       " + ase.getRequestId());
        } catch (AmazonClientException ace) {
            System.out.println("Caught an AmazonClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with S3, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message: " + ace.getMessage());
        }
    }

    /**
     * Creates a temporary file with text data to demonstrate uploading a file
     * to Amazon S3
     *
     * @return A newly created temporary file with text data.
     *
     * @throws IOException
     */
    private static File createSampleFile() throws IOException {
        File file = File.createTempFile("aws-java-sdk-", ".txt");
        file.deleteOnExit();

        Writer writer = new OutputStreamWriter(new FileOutputStream(file));
        writer.write("abcdefghijklmnopqrstuvwxyz\n");
        writer.write("01234567890112345678901234\n");
        writer.write("!@#$%^&*()-=[]{};':',.<>/?\n");
        writer.write("01234567890112345678901234\n");
        writer.write("abcdefghijklmnopqrstuvwxyz\n");
        writer.close();

        return file;
    }

    /**
     * Displays the contents of the specified input stream as text.
     *
     * @param input
     *            The input stream to display as text.
     *
     * @throws IOException
     */
    private static void displayTextInputStream(InputStream input) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        while (true) {
            String line = reader.readLine();
            if (line == null) break;

            System.out.println("    " + line);
        }
        System.out.println();
    }

}

結果

f:id:m_shige1979:20170108161508p:plain


Spring Bootで画像を縮小して表示

画像を縮小する

そもそも画像制御とかプログラムでほとんど書かないけどね

実装

Image01Controller.java
package com.example.controller;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import org.springframework.core.io.ResourceLoader;
import javax.imageio.ImageIO;

@RestController
public class Image01Controller {
	
	@Autowired
	ResourceLoader resourceLoader;
	private int c;
		
	@ResponseBody
	@RequestMapping(value = "/getImageReducing/{id}", method = {RequestMethod.GET })
	public HttpEntity<byte[]> getImageReducing(@PathVariable String id) throws IOException {
		
		// リソースファイルを読み込み
		Resource resource = resourceLoader.getResource("classpath:" + "/static/image/" + id + ".jpg");
		InputStream image = resource.getInputStream();
		
		// BufferedImageへ設定
		BufferedImage bufferedImage1 = ImageIO.read(image);
		BufferedImage bufferedImage2 = null;
		
		// 変換情報を設定
		int width = 300;
		int height = 250;
		Image image1 = bufferedImage1.getScaledInstance(width, height, Image.SCALE_DEFAULT);
		bufferedImage2 = new BufferedImage(image1.getWidth(null), image1.getHeight(null), BufferedImage.TYPE_INT_RGB);
		
		// 縮小処理
		Graphics graphics2 = null;
		try {
			graphics2 = bufferedImage2.getGraphics();
			int x = 0;
			int y = 0;
			graphics2.drawImage(bufferedImage1, x, y, width, height, null);
		} finally {
			graphics2.dispose();
		}
		
		// byteへ変換
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		ImageIO.write(bufferedImage2, "jpg", bout);
		byte[] b = bout.toByteArray();
		
		// レスポンスデータとして返却
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.IMAGE_JPEG);
		headers.setContentLength(b.length);
		return new HttpEntity<byte[]>(b, headers);
	}
	
}

Spring BootでRestControllerで画像を出力する

簡単にresources配下にあるファイルを画面に表示する

Spring Bootの場合は@RestControllerを返す場合は指定のデータを返却するような感じになるので返り値をバイナリ形式で返してみる。

やること

  • 画像を読み込み
  • byte[]へ変換して設定
  • jpegデータとして返却

実装

pom.xml
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
Image01Controller.java
package com.example.controller;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import org.springframework.core.io.ResourceLoader;

@RestController
public class Image01Controller {
	
	@Autowired
	ResourceLoader resourceLoader;
	
	@ResponseBody
	@RequestMapping(value = "/getImage/{id}", method = {RequestMethod.GET })
	public HttpEntity<byte[]> getImage(@PathVariable String id) throws IOException {
		
		// リソースファイルを読み込み
		Resource resource = resourceLoader.getResource("classpath:" + "/static/image/" + id + ".jpg");
		InputStream image = resource.getInputStream();
		
		// byteへ変換
		byte[] b = IOUtils.toByteArray(image);
		
		// レスポンスデータとして返却
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.IMAGE_JPEG);		
		headers.setContentLength(b.length);
		return new HttpEntity<byte[]>(b, headers);
	}
	
}

わかったこと

  • RestControllerでバイナリを返すことができる
  • リソースファイルにアクセスするにはResourceLoaderを使用する
  • バイナリデータを返すことで画像などを表示することが可能

今回はここまで…

おまけ

ByteArrayOutputStreamでbyte[]変換した場合
package com.example.controller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import org.springframework.core.io.ResourceLoader;

@RestController
public class Image01Controller {
	
	@Autowired
	ResourceLoader resourceLoader;
	private int c;
	
	@ResponseBody
	@RequestMapping(value = "/getImage/{id}", method = {RequestMethod.GET })
	public HttpEntity<byte[]> getImage(@PathVariable String id) throws IOException {
		
		// リソースファイルを読み込み
		Resource resource = resourceLoader.getResource("classpath:" + "/static/image/" + id + ".jpg");
		InputStream image = resource.getInputStream();
		
		// byteへ変換
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		int c;
		while ((c = image.read()) != -1) {
			bout.write(c);
		}
		byte[] b = bout.toByteArray();
		
		// レスポンスデータとして返却
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.IMAGE_JPEG);
		headers.setContentLength(b.length);
		return new HttpEntity<byte[]>(b, headers);
	}
	
}

Spring BootのRestTemplateでバイナリデータを取得する

RestTemplate

Spring BootのAPI処理クラスと思われる

Rest APIの場合は基本JSONなんだけど

APIの結果としてはバイナリデータを受け取る場合もあるので取得したい

動作用API

LINE APIのGet ContentAPIを使用
https://devdocs.line.me/ja/?shell#get-content

実装サンプル

pom.xml
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
App
** RestTemplate
Spring BootのAPI処理クラスと思われる

** 実装サンプル
*** App
>|java|
package com.example;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

import org.apache.commons.io.IOUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class SampleSpringResttemplate01Application {

    public static void main(String[] args) throws IOException {
        SpringApplication.run(SampleSpringResttemplate01Application.class, args);
        
        // インスタンス生成
        RestTemplate restTemplate = new RestTemplate();

        // メッセージコンバーター設定
        restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());

        // ヘッダー設定
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
        headers.add("Authorization", "Bearer " + "アクセストークン");

        // パラメータ設定
        HttpEntity<String> entity = new HttpEntity<String>(headers);

        // URL
        String url;
        url = "https://api.line.me/v2/bot/message/999999999999999/content";

        // リクエスト発行
        ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.GET, entity, byte[].class, "1");
        
        // 戻り値よりチェック
        if(response.getStatusCode().equals(HttpStatus.OK)) {
            FileOutputStream output = new FileOutputStream(new File("filename.jpg"));
            IOUtils.write(response.getBody(), output);
        }
    }
}

所感

パッケージだけの場合はどのクラスかわかりにくいので動かすのがちょっと手間取ります。まあ、サンプルとして参考にさせていただいているのでわがままはいえませんけどw

Spring Bootでメールを送信する

Spring Bootでメールを送信する

今回の送信は簡単な平分を送信するだけ

メールサーバ

Gmailをリレーする

設定

application.properties
# port
server.port=7777

# mail
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=Gmailのメールアドレス
spring.mail.password=Gmailのパスワード
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

※Spring Bootではここに値を設定する

pom.xml

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-freemarker</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web-services</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

メール送信

MailSampleController.java
package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MailSampleController {
	
	private final JavaMailSender javaMailSender;

	@Autowired
	MailSampleController(JavaMailSender javaMailSender) {
		this.javaMailSender = javaMailSender;
	}
	
	@RequestMapping(value = "/mail/send", method = {RequestMethod.POST} )
	public String send() {
		
		SimpleMailMessage mailMessage = new SimpleMailMessage();

	    mailMessage.setTo("送信先のメールアドレス");
	    mailMessage.setReplyTo("リプライのメールアドレス");
	    mailMessage.setFrom("Fromのメールアドレス");
	    mailMessage.setSubject("テストメール");
	    mailMessage.setText("テストメールです、\nここから次の行\nおわりです\n");

	    javaMailSender.send(mailMessage);

		return "ok";
	}
	
}

このapiを叩けばメールを送信する。

結果

f:id:m_shige1979:20170107072733p:plain

所感

メール送信自体は今までなんどもあったけどローカルの開発環境で送信する手段があまり準備されていないことが多くて準備にドタバタしている感じになってしまう。テストとかする場合はもあるけどとりあえずはこれで…

MacよりGmailへメールを送信

備忘録

メッセージのソースだけだとあまりわからないので…

Postfix設定

設定ファイルバックアップ
sudo cp -p /etc/postfix/main.cf /etc/postfix/main.cf.org
/etc/postfix/main.cf
# リレーホストを追加
relayhost = [smtp.gmail.com]:587

#sasl setting
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_mechanism_filter = plain

#tls setting
smtp_use_tls = yes

※末尾に追加

/etc/postfix/sasl_passwd
[smtp.gmail.com]:587 ユーザーID@gmail.com:パスワード

※ユーザーIDとパスワードはそれぞれ任意のものを設定
※2段階認証している場合はアプリパスワードを発行すること

再作成
sudo chmod 640 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
再読込
sudo postfix reload

テスト

echo hogehoge | mail ユーザーID@gmail.com

macのpostfixでローカルメールを扱う

eclipseなどで開発する場合は

メールサーバの有無で開発環境に問題が発生しやすい
WindowsMacなどでもメールサーバは未実装の環境が多いので外部にはメールを送信したくないことある

基本的には

m-shige1979.hatenablog.com
これです

postfix

存在チェック
$ which postfix
/usr/sbin/postfix
$

あるのでそのまま

起動
sudo postfix start
設定ファイルバックアップ
sudo cp -p /etc/postfix/main.cf /etc/postfix/main.cf.org
/etc/postfix/main.cf編集
# 全てのメールを受け取る
inet_interfaces = all

# ローカル配送で不明なユーザを拒否しない
local_recipient_maps =

# Maildir形式として保存するディレクトリを /usr/local/mail/ の下にユーザー毎に作成する
mail_spool_directory = /usr/local/mail/

# ローカル配送で不明なユーザへのメールは maildev へ送る
luser_relay = maildev

# トランスポートマップを指定
transport_maps = hash:/etc/postfix/transport

※末尾に追加

/etc/postfix/transport
*       local:

※末尾に追加

postmapで作成
sudo postmap /etc/postfix/transport
メール保存先を準備
sudo mkdir -p /usr/local/mail/
sudo chmod 777 /usr/local/mail/
ユーザーを作成
sudo dscl . -create /Groups/users gid 1001
sudo dscl . -create /Users/maildev
sudo dscl . -create /Users/maildev RealName maildev
sudo dscl . -create /Users/maildev UniqueID 1001
sudo dscl . -create /Users/maildev PrimaryGroupID 1001
sudo dscl . -create /Users/maildev NFSHomeDirectory /Users/maildev
sudo dscl . -create /Users/maildev UserShell /bin/bash
sudo passwd maildev
sudo createhomedir -b -u maildev

※500番台を使用するとシステムが使えなくなる恐れがあるので気をつける必要があります。

リロード
sudo postfix reload

dovecot確認

brew install
$ brew install dovecot
READMEを確認
$ cat /usr/local/etc/dovecot/README
Configuration files go to this directory. See example configuration files in
/usr/local/Cellar/dovecot/2.2.27/share/doc/dovecot/example-config/
$
移動
cp /usr/local/Cellar/dovecot/2.2.27/share/doc/dovecot/example-config/dovecot.conf /usr/local/etc/dovecot/
cp -r /usr/local/Cellar/dovecot/2.2.27/share/doc/dovecot/example-config/conf.d /usr/local/etc/dovecot/
起動
sudo brew services start dovecot
/usr/local/etc/dovecot/dovecot.conf
protocols = imap pop3
mail_location = maildir:/usr/local/mail/%u
再起動
sudo brew services restart dovecot

テスト

$ telnet localhost 25
Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 matsumotoshigejinoMac-mini.local ESMTP Postfix
helo localhost
250 matsumotoshigejinoMac-mini.local
mail from: aaaa@test1.com
250 2.1.0 Ok
rcpt to: bbb@hoge.com
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
subject: test2
aaaaaa
ssssssssss
ddddd
.
250 2.0.0 Ok: queued as 5AA2D4F6580
quit
221 2.0.0 Bye
Connection closed by foreign host.
$
メール確認
$ ll /usr/local/mail/maildev/new/
total 8
-rw-------  1 maildev  601  443  1  7 02:21 1483723300.V1000008I4f6595M15495.matsumotoshigejinoMac-mini.local
$
ソース内容
Return-Path: <aaaa@test1.com>
X-Original-To: bbb@hoge.com
Delivered-To: bbb@hoge.com
Received: from localhost (localhost [IPv6:::1])
	by matsumotoshigejinoMac-mini.local (Postfix) with SMTP id 5AA2D4F6580
	for <bbb@hoge.com>; Sat,  7 Jan 2017 02:21:02 +0900 (JST)
subject: test2
Message-Id: <20170106172111.5AA2D4F6580@matsumotoshigejinoMac-mini.local>
Date: Sat,  7 Jan 2017 02:21:02 +0900 (JST)
From: aaaa@test1.com

aaaaaa
ssssssssss
ddddd

こんな感じ?