Android CameraX FTPにてNAS接続1

今日の朝は気持ちいがいいという顔をしている柴犬です。
鳥が囀る声が聞こえる中で気分が落ち着くようです。
カモの散歩
昨日、柴犬の夜の散歩の途中で不思議な光景に出会いました。下の映像に紹介していますカモの行列(散歩)です。
こんなに暗いのに鳥が動き回るとは驚きです。柴犬の散歩ですから隣に柴犬がいます。けれども何の恐れもなく逃げないでゆっくりと遊歩道を横断しています。
概要
前回 Android CameraX SMBにてNAS接続2 で SMB で接続しましたが、FTP でも接続をしてみます。
SMB のコードの時に、いろいろ覚えましたので少し手を加えるだけでよかったです。
MainActivity.kt に FtpAccess() 関数 を加えるとともに、initstart() 関数に FtpAccess() 起動を加えました。
MainActivity.kt
package siba.inu.android.sibainuapp2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import kotlinx.coroutines.*
import java.util.Properties
import jcifs.context.BaseContext
import jcifs.smb.NtlmPasswordAuthenticator
import jcifs.smb.SmbFile
import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
class MainActivity : AppCompatActivity() {
// コルーチン関連
private val sibainujob = Job()
private val sibainuscope = CoroutineScope(Dispatchers.Default + sibainujob)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initstart()
}
private fun initstart() {
findViewById<Button>(R.id.button).setOnClickListener() {
// 非同期処理
sibainuscope.launch {
NasAccess("user",
"password",
"domain",
"smbroot")
FtpAccess("ftpUsername",
"ftpPassword",
"ftpServer",
"ftpDirectory")
}
}
}
override fun onDestroy() {
sibainujob.cancel()
super.onDestroy()
}
//domain "192.168.□□.□□"
//smbroot "sm: //192.168.□□.□□/□□□□□□□□□□□□□/"
private fun NasAccess(user: String,
password: String,
domain: String,
smbroot: String) {
val prop = Properties()
prop.setProperty("jcifs.smb.client.minVersion", "SMB202")
prop.setProperty("jcifs.smb.client.maxVersion", "SMB311")
val bc = BaseContext(jcifs.config.PropertyConfiguration(prop))
val auth = NtlmPasswordAuthenticator(domain, user, password)
val cifsCon = bc.withCredentials(auth)
try {
val sf = SmbFile(smbroot, cifsCon)
Log.d("ServerFileAccess", sf.server)
Log.d("ServerFileAccess", sf.share)
Log.d("ServerFileAccess", sf.name)
Log.d("ServerFileAccess", sf.path)
if (sf.exists()) { // ←←←←←ここでエラー
Log.d("ServerFileAccess", "ファイル有")
val filenames: Array<String> = sf.list()
for (i in filenames.indices) {
Log.d("ServerFileAccess",filenames[i])
}
} else {
Log.d("ServerFileAccess", "ファイル無")
}
}catch (ex: Exception) {
Log.d("ServerFileAccess", ex.toString())
ex.printStackTrace()
} finally {
Log.d("ServerFileAccess", "finally")
}
}
//ftpServer "192.168.□□.□□"
//ftpDirectory "ftp://192.168.□□.□□/□□□□□□□□□□□□□/"
private fun FtpAccess(ftpUsername: String,
ftpPassword: String,
ftpServer: String,
ftpDirectory: String) {
val ftpClient = FTPClient()
try {
ftpClient.connect(ftpServer)
ftpClient.login(ftpUsername, ftpPassword)
ftpClient.enterLocalPassiveMode()
ftpClient.setFileType(FTP.BINARY_FILE_TYPE)
ftpClient.changeWorkingDirectory(ftpDirectory)
Log.d("ServerFileAccess", ftpClient.status)
//NASにファイルをアップロード
//val inputStream = FileInputStream(file)
//val fileName = file.name
//ftpClient.storeFile(fileName, inputStream)
//inputStream.close()
} catch (ex: Exception) {
Log.d("ServerFileAccess", ex.toString())
ex.printStackTrace()
} finally {
Log.d("ServerFileAccess", "finally")
ftpClient.logout()
ftpClient.disconnect()
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- uses-permission android:name="android.permission.ADD_SYSTEM_SERVICE"/ -->
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Sibainuapp2"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
build.gradle.kts(Module :app)
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "siba.inu.android.sibainuapp2"
compileSdk = 34
defaultConfig {
applicationId = "siba.inu.android.sibainuapp2"
minSdk = 26
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = 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"
}
buildFeatures {
viewBinding = true
}
}
dependencies {
//implementation("androidx.activity:activity-ktx:1.2.2")
//implementation("androidx.fragment:fragment-ktx:1.3.2")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.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")
// Apache クライアント
implementation(files("libs/commons-net-3.10.0.jar"))
// Java
implementation(files("libs/bcprov-jdk15on-1.70.jar"))
// implementation(files("libs/slf4j-api-2.0.11.jar"))
implementation("org.slf4j:slf4j-api:1.8.0-beta2")
runtimeOnly("org.jlib:jlib-awslambda-logback:1.0.0")
// SMB
//https://mvnrepository.com/artifact/eu.agno3.jcifs/jcifs-ng/2.1.7
//implementation(files("libs/jcifs-ng-2.1.10.jar"))
implementation(files("libs/jcifs-ng-2.1.7.jar"))
// コルーチン
val kotlin_version = "1.3.9"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${kotlin_version}")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:${kotlin_version}")
//implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC2")
val lifecycle_version = "2.7.0"
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:${lifecycle_version}")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:${lifecycle_version}")
}
FtpAccess()関数をエミュレートで実行

やりました。FTPで接続してSTATUSを取得できました。
私の Synology の NAS の設定はFTPサービスを有効にする(暗号化しない)を選ばないと接続できませんでした。

一覧表を出してみる
MainActivity.kt の FtpAccess() 関数の try 文
try {
ftpClient.connect(ftpServer)
ftpClient.login(ftpUsername, ftpPassword)
ftpClient.enterLocalPassiveMode()
ftpClient.setFileType(FTP.BINARY_FILE_TYPE)
ftpClient.changeWorkingDirectory(ftpDirectory)
Log.d("ServerFileAccess", ftpClient.status)
//NASにファイルをアップロード
//val inputStream = FileInputStream(file)
//val fileName = file.name
//ftpClient.storeFile(fileName, inputStream)
//inputStream.close()
}
に次の文を挿入します。
val filenames: Array<String> = ftpClient.listNames()
for (i in filenames.indices) {
Log.d("ServerFileAccess",filenames[i])
}
try {
ftpClient.connect(ftpServer)
ftpClient.login(ftpUsername, ftpPassword)
ftpClient.enterLocalPassiveMode()
ftpClient.setFileType(FTP.BINARY_FILE_TYPE)
ftpClient.changeWorkingDirectory(ftpDirectory)
Log.d("ServerFileAccess", ftpClient.status)
// ここに挿入
val filenames: Array<String> = ftpClient.listNames()
for (i in filenames.indices) {
Log.d("ServerFileAccess",filenames[i])
}
//NASにファイルをアップロード
//val inputStream = FileInputStream(file)
//val fileName = file.name
//ftpClient.storeFile(fileName, inputStream)
//inputStream.close()
}
何と、NAS の直下のフォルダー・ファイルが表示されました。
SMB とは異なるようです。

見直し
FtpAccess() の引数 ftpDirectory の書式を変えてみました。
//ftpDirectory "ftp://192.168.□□.□□/□□□□□□□□□□□□□/"
を次のように変えます。
ftp://192.168.□□.□□ を削除
//ftpDirectory "/□□□□□□□□□□□□□/"
例えば "/photo/myphoto/□□□□□□□□□□□□□/"
とすると次のようにフォルダーの中のファイルが表示できるようになりました。SMB とは指定の書式が違うようです。


一段落しましたので、これで終わりとします。