Androidでminioにデータを送るのは簡単なんだ．でも依存関係で地獄を見た．

# はじめに
始めまして．
この度はAdvent Calendar[「Qiita学生iOS&Androidエンジニアのカレンダー」](https://qiita.com/advent-calendar/2023/android_ios_students)に誘われたので初めてQiitaに記事を書こうと思います．

## minioとは
オンプレミスなAWS S3です．
[minioの公式サイト](https://min.io)

画像とか動画とかAPIで通信するには適さないなぁ〜
でもAWS契約するのもなぁ〜
自宅にサーバあるのになぁ〜
って人におすすめです．

## 今回は何を？
minioのサーバがあるとして，Androidでminioにデータ(CSVファイル)を送信してみようと思います

[minioのJavaクイックスタートドキュメント](https://min.io/docs/minio/linux/developers/java/minio-java.html)

## はじめよう

まずはGradleにminioの依存関係を書きましょう．
`implementation 'io.minio:minio:8.5.1'`
バージョンはその都度変えてください．

では，依存関係を書いたGradleです．

```Gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.minio_client'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.minio_client"
        minSdk 31
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    packagingOptions {
        exclude 'javax/servlet/jsp/resources/jspxml.xsd'
        exclude 'javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd'
        exclude 'javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd'
        exclude 'javax/servlet/http/LocalStrings_ja.properties'
        exclude 'META-INF/mimetypes.default'
        exclude 'javax/servlet/resources/web-app_2_4.xsd'
        exclude 'javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd'
        exclude 'javax/servlet/resources/j2ee_web_services_client_1_1.xsd'
        exclude 'javax/servlet/http/LocalStrings_fr.properties'
        exclude 'javax/servlet/LocalStrings_ja.properties'
        exclude 'javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd'
        exclude 'javax/servlet/http/LocalStrings_es.properties'
        exclude 'javax/servlet/resources/xml.xsd'
        exclude 'javax/servlet/http/LocalStrings.properties'
        exclude 'javax/servlet/resources/XMLSchema.dtd'
        exclude 'javax/servlet/resources/j2ee_web_services_1_1.xsd'
        exclude 'javax/servlet/resources/web-app_2_2.dtd'
        exclude 'javax/servlet/resources/j2ee_1_4.xsd'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'javax/servlet/resources/web-app_2_5.xsd'
        exclude 'javax/el/LocalStrings.properties'
        exclude 'javax/servlet/jsp/resources/jspxml.dtd'
        exclude 'javax/servlet/resources/datatypes.dtd'
        exclude 'javax/servlet/jsp/resources/jsp_2_0.xsd'
        exclude 'javax/servlet/LocalStrings_fr.properties'
        exclude 'javax/servlet/LocalStrings.properties'
        exclude 'javax/servlet/jsp/resources/jsp_2_1.xsd'
        exclude 'javax/servlet/resources/web-app_2_3.dtd'

    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.appcompat:appcompat:1.6.0'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    implementation 'io.minio:minio:8.5.1'
    implementation group: 'org.apache.openejb', name: 'javaee-api', version: '6.0-1'
    implementation 'com.fasterxml.woodstox:woodstox-core:6.5.0'

}
```

あれ？？？すごく長くなっちゃった．
と，いうのも
僕が実装してみた時にライブラリ不足や競合が多く発生してしまい，それを解消したのがこちらです．

なぜこうなったのかを知りたい方はその時の苦悩が[ここ](https://teratail.com/questions/p9epwfntvo3n7n)にあるので見てください．

また，すでにある程度大きいプログラムで依存関係も複雑になってる場合これを実装すると競合が起こると思います．
僕はその時[これ](https://zenn.dev/log_suzaki/scraps/3b614a9664a601)で解消しました．

## ではクライアントクラスを作ろう

```kotlin
package com.example.minio_client

import io.minio.MinioClient
import io.minio.UploadObjectArgs

class MinioClient {

    val accessKey = "アクセスキー"
    val secretKey = "シークレットキー"
    val bucketName = "バケットの名前"
    val endpoint = "https://~~~~"

    fun postFile(pass: String): String {
        val minioClient = MinioClient.builder()
            .endpoint(endpoint)
            .credentials(accessKey, secretKey)
            .build()

        // パスの後ろのファイル名をもらっておく
        val fileName = pass.substringAfterLast("/")

        try {
            minioClient.uploadObject(
                UploadObjectArgs.builder()
                    .bucket(bucketName)
                    // ファイルの名前．"/"で区切ると階層を分けられる
                    .`object`("$fileName.csv")
                    // ファイルの場所
                    .filename(pass)
                    .build()
            )
            // 送信成功したらデータの場所を返してくれる
            return "$endpoint/$bucketName/$fileName"
        } catch (e: Exception) {
            return "File upload failed: ${e.message}"
        }
    }
}
```

クラスを作ったのでActivityで読んであげましょう．

```kotlin
package com.example.minio_client

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val externalStorage = ExternalStorage(this)
        val minioClient = MinioClient()

        try {
            val url = minioClient.postFile("CSVファイルの場所")
            // あの関数はファイルの場所を返してくれる
            Log.d("minio", "url = $url")
        } catch (e: Exception) {
            Log.d("minio", "error = ${e.message}")
        }

    }
}
```

# おわりに

おそらくもっといいやり方があります．
当時は締め切りに追われて頭を焦がしながらこれを作りました．
もっといいやり方を知っている方や依存関係の問題を詳しく知っている人がいればコメントお願いします．