Skip to content

Instantly share code, notes, and snippets.

@Showichiro
Created October 31, 2024 09:19
Show Gist options
  • Select an option

  • Save Showichiro/56797e7ad5a2c7a00fcae2fac49c72de to your computer and use it in GitHub Desktop.

Select an option

Save Showichiro/56797e7ad5a2c7a00fcae2fac49c72de to your computer and use it in GitHub Desktop.
Spring Integrationを用いたFTP接続のテスト

Spring BootのサーバでFTP接続をして接続先のファイルの書き換え操作を実装したいです。グーグル検索では、SpringIntegrationのFTPアダプターというライブラリが出てきましたが、認識合っていますか。


はい、その認識で合っています。Spring IntegrationのFTPアダプターは、Spring BootアプリケーションでFTP操作を実装する一般的な方法の1つです。

シンプルなFTPクライアントの実装例を示します。

// build.gradle
dependencies {
    implementation 'org.springframework.integration:spring-integration-ftp'
    implementation 'org.springframework.boot:spring-boot-starter-integration'
}

// FTPConfig.java
@Configuration
public class FTPConfig {
    
    @Value("${ftp.host}")
    private String host;
    
    @Value("${ftp.port}")
    private int port;
    
    @Value("${ftp.username}")
    private String username;
    
    @Value("${ftp.password}")
    private String password;
    
    @Bean
    public DefaultFtpSessionFactory ftpSessionFactory() {
        DefaultFtpSessionFactory factory = new DefaultFtpSessionFactory();
        factory.setHost(host);
        factory.setPort(port);
        factory.setUsername(username);
        factory.setPassword(password);
        return factory;
    }
    
    @Bean
    public FtpTemplate ftpTemplate(DefaultFtpSessionFactory ftpSessionFactory) {
        return new FtpTemplate(ftpSessionFactory);
    }
}

// FTPService.java
@Service
@Slf4j
public class FTPService {
    
    @Autowired
    private FtpTemplate ftpTemplate;
    
    // ファイルの読み込み
    public String readFile(String remoteFilePath) {
        try {
            return ftpTemplate.execute(session -> {
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                session.read(remoteFilePath, outputStream);
                return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
            });
        } catch (Exception e) {
            log.error("Failed to read file: " + remoteFilePath, e);
            throw new RuntimeException("File read error", e);
        }
    }
    
    // ファイルの書き込み
    public void writeFile(String remoteFilePath, String content) {
        try {
            ftpTemplate.execute(session -> {
                InputStream inputStream = new ByteArrayInputStream(
                    content.getBytes(StandardCharsets.UTF_8)
                );
                session.write(inputStream, remoteFilePath);
                return null;
            });
        } catch (Exception e) {
            log.error("Failed to write file: " + remoteFilePath, e);
            throw new RuntimeException("File write error", e);
        }
    }
    
    // ファイルの削除
    public void deleteFile(String remoteFilePath) {
        try {
            ftpTemplate.execute(session -> {
                session.remove(remoteFilePath);
                return null;
            });
        } catch (Exception e) {
            log.error("Failed to delete file: " + remoteFilePath, e);
            throw new RuntimeException("File delete error", e);
        }
    }
}

// application.yml
ftp:
  host: ftp.example.com
  port: 21
  username: your-username
  password: your-password

この実装には以下の特徴があります:

  1. Spring IntegrationのFtpTemplateを使用して、FTP操作を抽象化しています。
  2. 設定はapplication.ymlで外部化できます。
  3. 主な機能:
    • ファイルの読み込み
    • ファイルの書き込み
    • ファイルの削除

使用例:

@RestController
public class FTPController {
    @Autowired
    private FTPService ftpService;
    
    @PostMapping("/update-file")
    public void updateFile(@RequestParam String filePath, @RequestBody String content) {
        ftpService.writeFile(filePath, content);
    }
}

これ以外にも必要な機能がありましたら、例えば:

  • ディレクトリの作成/削除
  • ファイルの移動/コピー
  • ファイル一覧の取得

などの機能を追加することができます。具体的にどのような操作が必要でしょうか?


FTPサーバはDockerで構築しており、docker-compose.yamlは以下のように定義しているのですが、アプリから打鍵すると421が発生します。どのような原因が考えられますか

services:
  ftp_server:
    image: stilliard/pure-ftpd
    container_name: ftp-server
    ports:
      - "21:21"
      - "20:20"
      - "30000-30009:30000-30009" # 30000~30009のポートは、このDockerイメージのデフォルト・パッシブ・ポートがアサインされているために必要

    environment:
      - PUBLICHOST=host.docker.internal
      - FTP_USER_NAME=uname
      - FTP_USER_PASS=uP@ssw0rd
      - FTP_USER_HOME=/home/ubuntu

    restart: always

421エラーはFTP接続の確立に失敗したことを示すエラーで、この場合いくつかの原因が考えられます。主な確認ポイントは以下の通りです:

  1. PASVモード設定: Spring Bootアプリケーションがアクティブモードではなくパッシブモード(PASV)を使用するように設定する必要があります。以下のように設定を修正してみましょう:
// FTPConfig.java
@Configuration
public class FTPConfig {
    
    @Value("${ftp.host}")
    private String host;
    
    @Value("${ftp.port}")
    private int port;
    
    @Value("${ftp.username}")
    private String username;
    
    @Value("${ftp.password}")
    private String password;
    
    @Bean
    public DefaultFtpSessionFactory ftpSessionFactory() {
        DefaultFtpSessionFactory factory = new DefaultFtpSessionFactory();
        factory.setHost(host);
        factory.setPort(port);
        factory.setUsername(username);
        factory.setPassword(password);
        
        // パッシブモードの設定
        factory.setClientMode(FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE);
        
        // 接続タイムアウトの設定
        factory.setConnectTimeout(5000);
        
        // 追加のFTPクライアント設定
        factory.setControlEncoding("UTF-8");
        factory.setDefaultTimeout(5000);
        factory.setDataTimeout(5000);
        
        return factory;
    }
    
    @Bean
    public FtpTemplate ftpTemplate(DefaultFtpSessionFactory ftpSessionFactory) {
        FtpTemplate template = new FtpTemplate(ftpSessionFactory);
        template.setAutoCreateDirectory(true);
        return template;
    }
}

// application.yml
ftp:
  host: localhost  # Docker使用時はlocalhostを指定
  port: 21
  username: uname
  password: uP@ssw0rd
  1. docker-compose.ymlの修正:
services:
  ftp_server:
    image: stilliard/pure-ftpd
    container_name: ftp-server
    ports:
      - "21:21"
      - "20:20"
      - "30000-30009:30000-30009"
    environment:
      - PUBLICHOST=localhost  # host.docker.internalではなくlocalhostを試してください
      - FTP_USER_NAME=uname
      - FTP_USER_PASS=uP@ssw0rd
      - FTP_USER_HOME=/home/ubuntu
      - ADDED_FLAGS=-O w3c:/var/log/pure-ftpd/transfer.log  # ログ出力を有効化
    volumes:
      - ./ftp/data:/home/ubuntu  # データディレクトリをマウント
      - ./ftp/logs:/var/log/pure-ftpd  # ログディレクトリをマウント
    restart: always
  1. 追加の確認ポイント:
  • ファイアウォールの設定:

    • ポート21(制御接続)
    • ポート30000-30009(パッシブモードのデータ接続)が開いているか確認
  • ネットワーク接続:

# FTPサーバーへの接続テスト
telnet localhost 21

# コンテナのログ確認
docker logs ftp-server
  • FTPクライアントでのテスト:
    • FileZillaなどのFTPクライアントで接続テストを行い、Spring Boot以外でも同じ問題が発生するか確認
  1. トラブルシューティング手順:

  2. まず、Docker コンテナが正常に起動していることを確認:

docker ps
  1. コンテナのログを確認:
docker logs ftp-server
  1. FTPサーバーの設定を確認:
docker exec -it ftp-server /bin/bash
cat /etc/pure-ftpd/conf/*
  1. Spring Bootアプリケーションのデバッグログを有効化:
# application.yml
logging:
  level:
    org.springframework.integration.ftp: DEBUG
    org.apache.commons.net.ftp: DEBUG

これらの設定を試してみても問題が解決しない場合は、具体的なエラーメッセージやログを共有していただけると、より詳細な原因究明が可能です。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment