メモ : 【Java】SFTPでRaspberry Piのデータを取得する

#**背景**
職場の方がラズベリーパイで大気やら紫外線やらのデータを収集しています。
そのデータを拝借しようと当初WinSCPでローカルにコピーしてたんですが、回数こなすうちに
億劫になったので、SFTPでデータを取得しようと考えました。そのコードを書き残します。

![ラズパイのデータコピーしてたんですけども.png](https://qiita-image-store.s3.amazonaws.com/0/237418/aafad03b-0313-7295-4298-690e8c2a2704.png)

　　　　　　　**SSH**...**暗号化された通信**　　　**FTP**...**ファイル転送プロトコル**

　　　　　　　**SSH + FTP = SFTP　　　SSHで暗号化された通信で、ファイルの転送を行うこと**

#**ラズパイのデータを標準出力する**

現在こんな感じでラズパイにデータが入ってます。
今回は20180716.csvというファイルのデータを出力してみます。
![1.png](https://qiita-image-store.s3.amazonaws.com/0/237418/8eaae85e-c0ad-62a9-8308-f5b548c5b979.png)
　-　**[Java SFTPでアップロード・ダウンロード(known_hosts不要)](https://qiita.com/tool-taro/items/a3f016a58e3e32675858)**
　-　**[Java+JSchでsftpによるファイル操作を行う](https://qiita.com/nenokido2000/items/a00348c9f6a0f942773b)**
　:grinning:こちらのページを参考にさせて頂きました。ありがとうございます。

※JavaでSFTPでのファイル転送するためにJSchというライブラリを使います。
まずは、**[こちら](http://www.jcraft.com/jsch/)**の**Download**からjarファイルをダウンロード(※2018/08/01時点の最新はjsch-0.1.54.jarです)
英語ばっかりで何書いてるか分かんないって方は[翻訳](https://qiita.com/tedkuma/items/5774f29f09fbf7d2a743)してくださいね。

pom.xmlの**`<dependencies>`**タグ内にjschの**`<dependency>`**タグを追加します。こんな感じで

```pom.xml
    <dependencies>
		<!--JSch_adding_at_self_-->
		<dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.54</version>
        </dependency>
    </dependencies>
```
コードはほぼ参考ページのをコピーさせていただきました。

```test.java
import com.jcraft.jsch.*;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class test {

    public static void main(String[] args) throws JSchException, SftpException, FileNotFoundException, IOException {

        String host = "192.168.29.29"; 
        int port = 22;
        String user = "pi";
        String password = "raspberry";
        String dir = "/home/pi/iot/pressure/data/2018/";
        String fileName = "20180716.csv";

        JSch jsch;
        Session session = null;
        ChannelSftp channel = null;
        FileInputStream fin = null;
        BufferedInputStream bin = null;

        try {
            //接続
            jsch = new JSch();
            session = jsch.getSession(user, host, port);
            session.setConfig("StrictHostKeyChecking", "no");//known_hostsのチェックをスキップ
            session.setPassword(password);
            session.connect();

            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();
            channel.cd(dir);
          
            //ダウンロード
            bin = new BufferedInputStream(channel.get(fileName));
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int length;
            while (true) {
                length = bin.read(buf);
                if (length == -1) {
                    break;
                }
                bout.write(buf, 0, length);
            }
            //標準出力
            System.out.format("%1$s", new String(bout.toByteArray(), StandardCharsets.UTF_8));
        }
        finally {
            if (fin != null) {
                try {
                    fin.close();
                }
                catch (IOException e) {
                }
            }
            if (bin != null) {
                try {
                    bin.close();
                }
                catch (IOException e) {
                }
            }
            if (channel != null) {
                try {
                    channel.disconnect();
                }
                catch (Exception e) {
                }
            }
            if (session != null) {
                try {
                    session.disconnect();
                }
                catch (Exception e) {
                }
            }
        }
    }
}
```
では実行してみます。CSVの内容が書き出されました。
![2.png](https://qiita-image-store.s3.amazonaws.com/0/237418/24cb4daa-2c51-453a-c72a-6434789b3207.png)

#**ラズパイのファイルをローカルに出力する**
今度はデータをローカルのファイルに出力してみます。
上のコードの標準出力という箇所を、ファイルに書出し 以下の内容に変更しただけです。

```test.java
import com.jcraft.jsch.*;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

public class test {

    public static void main(String[] args) throws JSchException, SftpException, FileNotFoundException, IOException {

        String host = "192.168.29.29";
        int port = 22;
        String user = "pi";
        String password = "raspberry";
        String dir = "/home/pi/iot/pressure/data/2018/";
        String fileName = "20180716.csv";

        JSch jsch;
        Session session = null;
        ChannelSftp channel = null;
        FileInputStream fin = null;
        BufferedInputStream bin = null;

        try {
            
            jsch = new JSch();
            session = jsch.getSession(user, host, port);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword(password);
            session.connect();

            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();
            channel.cd(dir);

            bin = new BufferedInputStream(channel.get(fileName));
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int length;
            while (true) {
                length = bin.read(buf);
                if (length == -1) {
                    break;
                }
                bout.write(buf, 0, length);
            }
            File file = new File("C:/tedkuma/BOX/★"+fileName);//ファイルに書出し
            file.createNewFile();
            FileWriter fw = new FileWriter(file);
            fw.write(new String(bout.toByteArray()));
            fw.close();
            System.out.print("出力成功");
        }
        finally {
            if (fin != null) {
                try {
                    fin.close();
                }
                catch (IOException e) {
                }
            }
            if (bin != null) {
                try {
                    bin.close();
                }
                catch (IOException e) {
                }
            }
            if (channel != null) {
                try {
                    channel.disconnect();
                }
                catch (Exception e) {
                }
            }
            if (session != null) {
                try {
                    session.disconnect();
                }
                catch (Exception e) {
                }
            }
        }
    }
}
```
コンソールに出力成功と表示され、対象のディレクトリに★の付いたファイルが出来ています。
![3.png](https://qiita-image-store.s3.amazonaws.com/0/237418/6aea5f67-58b9-ef47-6bf4-4b57009ea372.png)

中身も大丈夫そうです。先ほどと同じデータがCSV出力されています。
![4.png](https://qiita-image-store.s3.amazonaws.com/0/237418/f7a062d8-d9be-7611-3592-e91d603e871b.png)




#**更新日１日以内のファイルをローカルに出力する**
先日書いた [更新日が1日以内のファイルを特定する](https://qiita.com/tedkuma/items/b98cc4d0dee782323096) という記事の内容を流用して、
更新日１日以内のファイルだけをローカルに出力してみます。

[ChannelSftp.LsEntry](https://epaul.github.io/jsch-documentation/simple.javadoc/com/jcraft/jsch/ChannelSftp.LsEntry.html)のページが参考になります。
LsEntryからgetAttrs()でファイルの属性を受け取って、getMTime()で更新日が取得できます。

個人的につまづいたのは、ラズパイ側の情報を受け取ったときにfilelistの中に
ファイル以外に(.とか..の)ディレクトリを受け取ってエラーが出てしまったことです。
isDir()でディレクトリの判別が出来たので、ディレクトリでなければ処理をするという流れにしました。
![Rlogin.png](https://qiita-image-store.s3.amazonaws.com/0/237418/5771e5d4-8f1f-a823-f2d3-15f8e740d4e5.png)


```test.java
import com.jcraft.jsch.*;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Vector;

public class test {

    public static void main(String[] args) throws JSchException, SftpException, FileNotFoundException, IOException {

        String host = "192.168.29.29";
        int port = 22;
        String user = "pi";
        String password = "raspberry";
        String dir = "/home/pi/iot/pressure/data/2018/";

        JSch jsch;
        Session session = null;
        ChannelSftp channel = null;
        FileInputStream fin = null;
        BufferedInputStream bin = null;

        try {
            
            jsch = new JSch();
            session = jsch.getSession(user, host, port);
            session.setConfig("StrictHostKeyChecking", "no");   
            session.setPassword(password);
            session.connect();

            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();
            channel.cd(dir);

            Calendar st = Calendar.getInstance();   //Calendarクラスで現在日時を取得
            st.add(Calendar.DATE, -1);              //1日前を取得
            Date start = st.getTime();              //Dateに直す

            Calendar en = Calendar.getInstance();
            en.add(Calendar.MINUTE, -10);           //10分前を取得
            Date end = en.getTime();

            Vector<?> filelist = channel.ls(dir);
            for(int i=0; i<filelist.size();i++){

                LsEntry entry = (LsEntry) filelist.get(i);
                String filename= entry.getFilename();
                SftpATTRS attrs =  entry.getAttrs();
                Date lastModified = new Date(attrs.getMTime() * 1000L);
                if(attrs.isDir()) {
                    //ディレクトリだったら何もしない
                }if(start.compareTo(lastModified)<0 && end.compareTo(lastModified)>0){
                    bin = new BufferedInputStream(channel.get(filename)); 
                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    byte[] buf = new byte[1024];
                    int length;
                    while (true) {
                        length = bin.read(buf);
                        if (length == -1) {
                            break;
                        }
                        bout.write(buf, 0, length);
                    }
                    File file = new File("C:/tedkuma/★"+filename);
                    file.createNewFile();
                    FileWriter fw = new FileWriter(file);
                    fw.write(new String(bout.toByteArray()));
                    fw.close();
                    System.out.print("出力成功");
                }
            }
        }
        finally {
            if (fin != null) {
                try {
                    fin.close();
                }
                catch (IOException e) {
                }
            }
            if (bin != null) {
                try {
                    bin.close();
                }
                catch (IOException e) {
                }
            }
            if (channel != null) {
                try {
                    channel.disconnect();
                }
                catch (Exception e) {
                }
            }
            if (session != null) {
                try {
                    session.disconnect();
                }
                catch (Exception e) {
                }
            }
        }
    }
}
```
対象のディレクトリに更新日1日以内のファイルだけが★付きの名前でコピーされています。
![5.png](https://qiita-image-store.s3.amazonaws.com/0/237418/54cc818a-9d55-037c-c618-45ba4b2e40b8.png)

昨晩のデータが入っているので、データの中身も問題なさそうです。
![6.png](https://qiita-image-store.s3.amazonaws.com/0/237418/609a0fd0-d3a2-0f8e-13b2-1e06255ef617.png)