Android Java で CameraX の写真をキャプチャ1
なにか分からない文字がいっぱいあるな。なにしてんだという顔している柴犬です。
概要
言語Kotlin で CameraX を扱った例は WEB に記事がたくさんありますが、JAVA での記事は多くないので記録します。
最後 FTP で宅内ネットワークにある NAS にアップロードしたいのですが、クラス FtpAccess のcall関数のところでエラーになり上手くいきません。
クラス FtpAccess のコントラクターは「FtpAccess コントラクタ」のログがありますので処理できているようですが、call 関数が起動しません。
今後解決したら記録することにします。
1冊のみでは理解に苦しいところがあるので更に新しく本を購入しました。
翔泳社 齊藤 新三 著
2024年2月26日現在
WEBのみでは断片的で覚えにくいので最初に購入した Kotlin の本です。
AndroidManifest.xml
カメラ関係、ネット関係、ストレイジ関係と permission が必要です。
<?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-feature android:name="android.hardware.camera.any" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <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.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <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.Cameraxjavanas" 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(:app)
カメラ関係、FTP関係、ライフサイクル関係の依存関係を入れます。
plugins { id 'com.android.application' } android { namespace 'org.sibainu.relax.room.cameraxjavanas' compileSdk 34 defaultConfig { applicationId "org.sibainu.relax.room.cameraxjavanas" minSdk 26 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 } } dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation files('libs/commons-net-3.10.0.jar') implementation files('libs/jcifs-ng-2.1.7.jar') testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' def camerax_version = "1.1.0-beta01" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" implementation "androidx.camera:camera-video:${camerax_version}" implementation "androidx.camera:camera-view:${camerax_version}" implementation "androidx.camera:camera-extensions:${camerax_version}" def lifecycle_version = "2.7.0" implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:${lifecycle_version}") implementation("androidx.lifecycle:lifecycle-runtime-ktx:${lifecycle_version}") }
activity_main.xml
今回は、ボタン、テキストビュー、プレビューの3つのみです。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.camera.view.PreviewView android:id="@+id/viewFinder" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/bt_shutter" app:layout_constraintTop_toTopOf="parent"/> <Button android:id="@+id/bt_shutter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="4dp" android:text="@string/bt_shutter" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/tvinfo" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" /> <TextView android:id="@+id/tvinfo" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="264dp" android:layout_marginBottom="16dp" android:text="info" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
シンプルな表示です。
res/values/strings.xml
<resources> <string name="app_name">CameraXApp</string> <string name="take_photo">Take Photo</string> <string name="start_capture">Start Capture</string> <string name="stop_capture">Stop Capture</string> <string name="bt_shutter">Shutter</string> </resources>
MainActivity.java
ここは結構労力が必要でした。
protected void onCreate
package org.sibainu.relax.room.cameraxjavanas; import static java.lang.String.join; import android.Manifest; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCaptureException; import androidx.camera.core.Preview; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.view.PreviewView; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.google.common.util.concurrent.ListenableFuture; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Locale; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Represents a custom camera activity. This activity allows the user to capture images using the device's camera. */ public class MainActivity extends AppCompatActivity { private static String TAG = "camerax"; private static String FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"; private ImageCapture imagecapture; private Uri imageuri; private ExecutorService cameraExecutor; private Button bt; private final int REQUEST_CODE_PERMISSIONS = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); cameraExecutor = Executors.newSingleThreadExecutor(); if(ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){ String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}; ActivityCompat.requestPermissions(MainActivity.this, permissions, REQUEST_CODE_PERMISSIONS); return; } bt = findViewById(R.id.bt_shutter); clickListener ls = new clickListener(); bt.setOnClickListener(ls); startCamera(); }
private void startCamera
private void startCamera() { ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { // アプリケーションのプロセス内のカメラのライフサイクルをバインド ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); // Preview PreviewView previewView = findViewById(R.id.viewFinder); Preview preview = new Preview.Builder().build(); preview.setSurfaceProvider(previewView.getSurfaceProvider()); imagecapture = new ImageCapture.Builder().build(); // バックカメラをデフォルト CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; // 既存の破棄 cameraProvider.unbindAll(); // ビュー・イメージをカメラに結合 cameraProvider.bindToLifecycle(this, cameraSelector, preview, imagecapture); } catch (ExecutionException | InterruptedException e) { Log.e(TAG, e.getLocalizedMessage(), e); } }, ContextCompat.getMainExecutor(this)); }
private void takePhoto
@UiThread private void takePhoto() { if (imagecapture == null) { return; } Log.d(TAG, "imagecapture"); // Create time stamped name and MediaStore entry. SimpleDateFormat dateFormat = new SimpleDateFormat(FILENAME_FORMAT, Locale.JAPAN); // 現在の日時を取得。 Date now = new Date(System.currentTimeMillis()); // 取得した日時データを「yyyyMMddHHmmss」形式に整形した文字列を生成。 String nowStr = dateFormat.format(now); // ストレージに格納する画像のファイル名を生成。ファイル名の一意を確保するためにタイムスタンプの値を利用。 String name = "Photo_" + nowStr; Log.d(TAG,name); // ContentValuesオブジェクトを生成。 ContentValues values = new ContentValues(); // 画像ファイル名を設定。 values.put(MediaStore.Images.Media.TITLE, name); // 画像ファイルの表示名を設定。 values.put(MediaStore.Images.Media.DISPLAY_NAME, name); // 画像ファイルの種類を設定。 values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // 共有ストレージに保存するパス if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image"); } Log.d(TAG,"SDK_INT"); // ContentResolverオブジェクトを生成。 ContentResolver resolver = getContentResolver(); Log.d(TAG,"resolver"); // ContentResolverを使ってURIオブジェクトを生成。 imageuri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); Log.d(TAG,"imageuri"); // 画面遷移 Intentオブジェクトを生成。 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Log.d(TAG,"intent"); // Extra情報としてimageuriを設定。 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageuri); // file + metadata 出力オプション・オブジェクトの作成 ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(resolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values).build(); Log.d(TAG,"outputOptions"); TextView tv = findViewById(R.id.tvinfo); tv.setText("アウトプットオプション"); // 撮影後にトリガーされる画像キャプチャのリスナーを設定する imagecapture.takePicture( outputOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() { @Override public void onError(ImageCaptureException error) { Log.e(TAG, "Photo capture failed: " + error.toString(), error); } @Override public void onImageSaved(ImageCapture.OutputFileResults outputFileResults) { CharSequence msg = "Photo capture succeeded: " + outputFileResults.getSavedUri(); Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); Log.d(TAG, msg.toString()); Uri uri = Uri.parse(outputFileResults.getSavedUri().toString()); //Uri uri = outputFileResults.getSavedUri(); try { //InputStream ips = resolver.openInputStream(uri); String[] projection = {MediaStore.MediaColumns.DATA}; //アプリケーションのデータベースに接続して DATA を検索し格納されている path を取得 Cursor cursor = getContentResolver().query( imageuri, projection, null, null, null ); if (cursor != null) { String path = ""; //最初のレコードにカーソルを移します if (cursor.moveToFirst()) { //カラムは一つだけなのでインデックスは 0 path = cursor.getString(0).toString(); } cursor.close(); Log.d(TAG, path); //File file = new File(path); String filelist = ""; try { //InputStream ips = new FileInputStream(file); Log.d(TAG, "try"); filelist = new FtpAccess( "□□□□□□", "□□□□□□□□□□□□", "192.168.□□.□□", "photo/□□□□□□/", name + ".jpg", path).toString(); } catch (Exception e) { Log.d(TAG, "画像ファイルエラー"); } TextView tv = findViewById(R.id.tvinfo); tv.setText(filelist); } } catch (Exception ex) { Log.d(TAG, "アップロードエラー :", ex); } } }); }
class clickListener / protected void onDestroy
private class clickListener implements View.OnClickListener { @Override public void onClick(View view) { int id = view.getId(); if (id == R.id.bt_shutter) { Log.d(TAG,"onClick"); takePhoto(); } } } @Override protected void onDestroy() { super.onDestroy(); cameraExecutor.shutdown(); }
class FtpAccess
public class FtpAccess implements Callable<String> { private final String _ftpUsername; private final String _ftpPassword; private final String _ftpServer; private final String _ftpDirectory; private final String _filename; private final String _path; public FtpAccess(String ftpUsername, String ftpPassword, String ftpServer, String ftpDirectory, String filename, String path){ _ftpUsername = ftpUsername; _ftpPassword = ftpPassword; _ftpServer = ftpServer; _ftpDirectory = ftpDirectory; _filename = filename; _path = path; Log.d(TAG, "FtpAccess コンストラクタ"); } // 非同期内でセットした文字列を返す @WorkerThread @Override public String call() { Log.d(TAG, "call start"); //ファイルの一覧表を返します ArrayList<String> infolist = new ArrayList<>(); FTPClient ftpClient = new FTPClient(); try { Log.d(TAG, _ftpServer); //デフォルト ポートでリモート ホストに接続され、システムに割り当てられたポートで現在のホストから発信されるソケットを開きます ftpClient.connect(_ftpServer); Log.d(TAG, _ftpServer); //指定されたユーザーとパスワードを使用して FTP サーバーにログインします ftpClient.login(_ftpUsername, _ftpPassword); Log.d(TAG, _ftpPassword); //データ転送を行うために接続するデータ ポートを開くようにサーバーに指示されます ftpClient.enterLocalPassiveMode(); //多くの FTP サーバーはデフォルトで BINARY になっています ftpClient.setFileType(FTP.BINARY_FILE_TYPE); Log.d("ServerFileAccess", ftpClient.getStatus()); //ディレクトリ移動 ftpClient.changeWorkingDirectory(_ftpDirectory); Log.d(TAG, _ftpDirectory); // String[] filenames; filenames = ftpClient.listNames(); for (String filename : filenames) { Log.d("ServerFileAccess", filename); infolist.add(filename); } //NASにファイルをアップロード // FileInputStreamを取得する。 try (FileInputStream ips = new FileInputStream(_path)) { // staoreFile関数の呼び出しでアップロードする。 if (ftpClient.storeFile(_filename, ips)) { // ログ出力 Log.d(TAG, "Upload - " + _filename); } } } catch (Exception e) { e.printStackTrace(); } finally { Log.d("ServerFileAccess", "finally"); infolist.add("処理を終了します"); try { ftpClient.logout(); ftpClient.disconnect(); } catch (Exception e) { Log.d(TAG, "FTP開放エラー :", e); } } return join("\n",infolist); } }
MainActivity.java全体
package org.sibainu.relax.room.cameraxjavanas; import static java.lang.String.join; import android.Manifest; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCaptureException; import androidx.camera.core.Preview; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.view.PreviewView; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.google.common.util.concurrent.ListenableFuture; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Locale; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Represents a custom camera activity. This activity allows the user to capture images using the device's camera. */ public class MainActivity extends AppCompatActivity { private static String TAG = "camerax"; private static String FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"; private ImageCapture imagecapture; private Uri imageuri; private ExecutorService cameraExecutor; private Button bt; private final int REQUEST_CODE_PERMISSIONS = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); cameraExecutor = Executors.newSingleThreadExecutor(); if(ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){ String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}; ActivityCompat.requestPermissions(MainActivity.this, permissions, REQUEST_CODE_PERMISSIONS); return; } bt = findViewById(R.id.bt_shutter); clickListener ls = new clickListener(); bt.setOnClickListener(ls); startCamera(); } private void startCamera() { ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { // Used to bind the lifecycle of cameras to the lifecycle owner ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); // Preview PreviewView previewView = findViewById(R.id.viewFinder); Preview preview = new Preview.Builder().build(); preview.setSurfaceProvider(previewView.getSurfaceProvider()); imagecapture = new ImageCapture.Builder().build(); // Select back camera as a default CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; // Unbind use cases before rebinding cameraProvider.unbindAll(); // Bind use cases to camera cameraProvider.bindToLifecycle(this, cameraSelector, preview, imagecapture); } catch (ExecutionException | InterruptedException e) { Log.e(TAG, e.getLocalizedMessage(), e); } }, ContextCompat.getMainExecutor(this)); } @UiThread private void takePhoto() { if (imagecapture == null) { return; } Log.d(TAG, "imagecapture"); // Create time stamped name and MediaStore entry. SimpleDateFormat dateFormat = new SimpleDateFormat(FILENAME_FORMAT, Locale.JAPAN); // 現在の日時を取得。 Date now = new Date(System.currentTimeMillis()); // 取得した日時データを「yyyyMMddHHmmss」形式に整形した文字列を生成。 String nowStr = dateFormat.format(now); // ストレージに格納する画像のファイル名を生成。ファイル名の一意を確保するためにタイムスタンプの値を利用。 String name = "Photo_" + nowStr; Log.d(TAG,name); // ContentValuesオブジェクトを生成。 ContentValues values = new ContentValues(); // 画像ファイル名を設定。 values.put(MediaStore.Images.Media.TITLE, name); // 画像ファイルの表示名を設定。 values.put(MediaStore.Images.Media.DISPLAY_NAME, name); // 画像ファイルの種類を設定。 values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // 共有ストレージに保存するパス if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image"); } Log.d(TAG,"SDK_INT"); // ContentResolverオブジェクトを生成。 ContentResolver resolver = getContentResolver(); Log.d(TAG,"resolver"); // ContentResolverを使ってURIオブジェクトを生成。 imageuri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); Log.d(TAG,"imageuri"); // 画面遷移 Intentオブジェクトを生成。 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Log.d(TAG,"intent"); // Extra情報としてimageuriを設定。 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageuri); // file + metadata 出力オプション・オブジェクトの作成 ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(resolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values).build(); Log.d(TAG,"outputOptions"); // 撮影後にトリガーされる画像キャプチャのリスナーを設定する imagecapture.takePicture( outputOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() { @Override public void onError(ImageCaptureException error) { Log.e(TAG, "Photo capture failed: " + error.toString(), error); } @Override public void onImageSaved(ImageCapture.OutputFileResults outputFileResults) { CharSequence msg = "Photo capture succeeded: " + outputFileResults.getSavedUri(); Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); Log.d(TAG, msg.toString()); Uri uri = Uri.parse(outputFileResults.getSavedUri().toString()); //Uri uri = outputFileResults.getSavedUri(); try { //InputStream ips = resolver.openInputStream(uri); String[] projection = {MediaStore.MediaColumns.DATA}; //アプリケーションのデータベースに接続して DATA を検索し格納されている path を取得 Cursor cursor = getContentResolver().query( imageuri, projection, null, null, null ); if (cursor != null) { String path = ""; //最初のレコードにカーソルを移します if (cursor.moveToFirst()) { //カラムは一つだけなのでインデックスは 0 path = cursor.getString(0).toString(); } cursor.close(); Log.d(TAG, path); //File file = new File(path); String filelist = ""; try { //InputStream ips = new FileInputStream(file); Log.d(TAG, "try"); filelist = new FtpAccess( "□□□□□□", "□□□□□□□□□□□□", "192.168.□□.□□", "photo/□□□□□□/", name + ".jpg", path).toString(); } catch (Exception e) { Log.d(TAG, "画像ファイルエラー"); } TextView tv = findViewById(R.id.tvinfo); tv.setText(filelist); } } catch (Exception ex) { Log.d(TAG, "アップロードエラー :", ex); } } }); } private class clickListener implements View.OnClickListener { @Override public void onClick(View view) { int id = view.getId(); if (id == R.id.bt_shutter) { Log.d(TAG,"onClick"); takePhoto(); } } } @Override protected void onDestroy() { super.onDestroy(); cameraExecutor.shutdown(); } // takePhoto() から呼び出すようにしています public class FtpAccess implements Callable<String> { private final String _ftpUsername; private final String _ftpPassword; private final String _ftpServer; private final String _ftpDirectory; private final String _filename; private final String _path; public FtpAccess(String ftpUsername, String ftpPassword, String ftpServer, String ftpDirectory, String filename, String path){ _ftpUsername = ftpUsername; _ftpPassword = ftpPassword; _ftpServer = ftpServer; _ftpDirectory = ftpDirectory; _filename = filename; _path = path; Log.d(TAG, "FtpAccess コンストラクタ"); } // 非同期内でセットした文字列を返す @WorkerThread @Override public String call() { Log.d(TAG, "call start"); //ファイルの一覧表を返します ArrayList<String> infolist = new ArrayList<>(); FTPClient ftpClient = new FTPClient(); try { Log.d(TAG, _ftpServer); //デフォルト ポートでリモート ホストに接続され、システムに割り当てられたポートで現在のホストから発信されるソケットを開きます ftpClient.connect(_ftpServer); Log.d(TAG, _ftpServer); //指定されたユーザーとパスワードを使用して FTP サーバーにログインします ftpClient.login(_ftpUsername, _ftpPassword); Log.d(TAG, _ftpPassword); //データ転送を行うために接続するデータ ポートを開くようにサーバーに指示されます ftpClient.enterLocalPassiveMode(); //多くの FTP サーバーはデフォルトで BINARY になっています ftpClient.setFileType(FTP.BINARY_FILE_TYPE); Log.d("ServerFileAccess", ftpClient.getStatus()); //ディレクトリ移動 ftpClient.changeWorkingDirectory(_ftpDirectory); Log.d(TAG, _ftpDirectory); // String[] filenames; filenames = ftpClient.listNames(); for (String filename : filenames) { Log.d("ServerFileAccess", filename); infolist.add(filename); } //NASにファイルをアップロード // FileInputStreamを取得する。 try (FileInputStream ips = new FileInputStream(_path)) { // staoreFile関数の呼び出しでアップロードする。 if (ftpClient.storeFile(_filename, ips)) { // ログ出力 Log.d(TAG, "Upload - " + _filename); } } } catch (Exception e) { e.printStackTrace(); } finally { Log.d("ServerFileAccess", "finally"); infolist.add("処理を終了します"); try { ftpClient.logout(); ftpClient.disconnect(); } catch (Exception e) { Log.d(TAG, "FTP開放エラー :", e); } } return join("\n",infolist); } } }
AsyncTask 使う方法(未確認)
非推奨な AsyncTask ですが、こちらも試してみようと思います。近々確認してみます。
public void FtpAccess() { Map<String, String> ftpmap = new HashMap<>(); ftpmap.put("ftpUsername","□□□□□□"); ftpmap.put("ftpPassword","□□□□□□□□□□□□"); ftpmap.put("ftpServer","192.168.□□.□□"); ftpmap.put("ftpDirectory","photo/□□□□□□/"); ftpmap.put("filename", name + ".jpg"); ftpmap.put("path",path); new FtpAsyncTask(){ @Override protected void onPostExecute(String response) { TextView tv = findViewById(R.id.tvinfo); tv.setText(response); } }.execute(new FtpTaskParams(ftpmap)); } //-------------------------------- public class FtpTaskParams { Map<String, String> _ftpData; public FtpTaskParams(Map<String, String> ftpData) { this._ftpData = ftpData; } } //-------------------------------- public class FtpAsyncTask extends AsyncTask<FtpTaskParams, String, String> { @Override protected String doInBackground(FtpTaskParams... params) { Map<String, String> ftpData = params[0]._ftpData; final String _ftpUsername = ftpData.get("ftpUsername"); final String _ftpPassword = ftpData.get("ftpPassword"); final String _ftpServer = ftpData.get("ftpServer"); final String _ftpDirectory = ftpData.get("ftpDirectory"); final String _filename = ftpData.get("filename"); final String _path = ftpData.get("path"); Log.d(TAG, "call start"); //ファイルの一覧表を返します ArrayList<String> infolist = new ArrayList<>(); FTPClient ftpClient = new FTPClient(); try { Log.d(TAG, _ftpServer); //デフォルト ポートでリモート ホストに接続され、システムに割り当てられたポートで現在のホストから発信されるソケットを開きます ftpClient.connect(_ftpServer); Log.d(TAG, _ftpServer); //指定されたユーザーとパスワードを使用して FTP サーバーにログインします ftpClient.login(_ftpUsername, _ftpPassword); Log.d(TAG, _ftpPassword); //データ転送を行うために接続するデータ ポートを開くようにサーバーに指示されます ftpClient.enterLocalPassiveMode(); //多くの FTP サーバーはデフォルトで BINARY になっています ftpClient.setFileType(FTP.BINARY_FILE_TYPE); Log.d("ServerFileAccess", ftpClient.getStatus()); //ディレクトリ移動 ftpClient.changeWorkingDirectory(_ftpDirectory); Log.d(TAG, _ftpDirectory); // String[] filenames; filenames = ftpClient.listNames(); for (String filename : filenames) { Log.d("ServerFileAccess", filename); infolist.add(filename); } //NASにファイルをアップロード // FileInputStreamを取得する。 try (FileInputStream ips = new FileInputStream(_path)) { // staoreFile関数の呼び出しでアップロードする。 if (ftpClient.storeFile(_filename, ips)) { // ログ出力 Log.d(TAG, "Upload - " + _filename); } } } catch (Exception e) { e.printStackTrace(); } finally { Log.d("ServerFileAccess", "finally"); infolist.add("処理を終了します"); try { ftpClient.logout(); ftpClient.disconnect(); } catch (Exception e) { Log.d(TAG, "FTP開放エラー :", e); } } return join("\n",infolist); } }
今回はここまでとします。