お初にお目にかかります。
会社にてアプリを作成している者で、何かあればここで検索しており非常に助かっております。
この度はどうしても解決できない問題があり、皆様のお力を借りたく質問を投稿いたしました。
何卒、お力添えいただきたく存じます。
前提・実現したいこと
実現したいこと:
SSL証明書を使用しているAzureサーバに、スマホ(Android)で撮影した写真をアップロードする
発生している問題・エラーメッセージ
問題:
撮影した写真の容量が大きい場合、送信に失敗することがある
容量は100kb(Mbではないです)を超えると必ず失敗します。
ただし、証明書を使用しない環境には問題なく送信ができております。
失敗時のエラー内容:
・ W/System.err: javax.net.ssl.SSLException: Write error
・ W/System.err: javax.net.ssl.SSLException: Read error
・connect pear
・タイムアウト
該当のソースコード
全部で3種類
・SendPic.java(写真撮影)
・AsyncHttps.java(送信用非同期処理)
・pic_upload.php(画像復元用)
流れ:
SendPicで撮影、byte配列に変換しAsyncHttpsに渡す(元のデータは残さない)
→AsyncHttpsでAzure web apps(SSL適用)上のphpファイルに送信
→phpファイルにて画像を復元
SendPic
1public class SendPic extends AppCompatActivity 2{ 3 static final int REQUEST_CAPTURE_IMAGE = 100; 4 5 File mediaFile; 6 private Uri imageUri; 7 8 9 @Override 10 protected void onCreate(Bundle savedInstanceState) 11 { 12 super.onCreate(savedInstanceState); 13 setContentView(R.layout.activity_main); 14 ImageButton btn_camera = findViewById(R.id.btn_camera); 15 16 17 //ボタンを押されたこと検知するリスナーを設置 18 btn_camera.setOnClickListener(BtnClickListener); 19 20 21 } 22 23 public View.OnClickListener BtnClickListener = new View.OnClickListener() 24 { 25 public void onClick(View v) 26 { 27 28 switch (v.getId()) 29 { 30 default: 31 return; 32 33 //承認された時用カメラボタン 34 case R.id.btn_camera: 35 Intent intent_c = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 36 File mediaStorageDir = new File 37 (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"MyApp"); 38 //カメラ画像を保存するディレクトリ 39 if(!mediaStorageDir.exists()) 40 { 41 if(!mediaStorageDir.mkdirs()) 42 {break;} 43 } 44 45 //現在日時を取得 46 String timeStamp = new SimpleDateFormat( 47 "yyyy_MM_dd_HH_mm_ss").format(new Date()); 48 49 //取得した現在日時をファイル名に 50 mediaFile = new File(mediaStorageDir.getPath() + File.separator 51 + timeStamp +".jpg"); 52 53 imageUri = Uri.fromFile(mediaFile); 54 55 Uri uri = FileProvider.getUriForFile( 56 SendPic.this 57 ,getApplicationContext().getPackageName() + ".provider" 58 , mediaFile); 59 intent_c.putExtra(MediaStore.EXTRA_OUTPUT, uri); 60 61 startActivityForResult(intent_c,REQUEST_CAPTURE_IMAGE); 62 break; 63 }; 64 }; 65 }; 66 67 @Override 68 public void onActivityResult(int requestCode, int resultCode, Intent resultData) 69 { 70 //写真撮った時 71 if(REQUEST_CAPTURE_IMAGE == requestCode && resultCode == Activity.RESULT_OK) 72 { 73 try 74 { 75 Bitmap bitmap= getBitmapFromUri(imageUri); 76 if(bitmap == null) 77 {return;} 78 79 iv1.setScaleType(ImageView.ScaleType.MATRIX); 80 iv1.setImageBitmap(bitmap); 81 //画像のサイズを今の1/3に変更し 82 final Bitmap changeScale = Bitmap.createScaledBitmap(bitmap,bitmap.getWidth()/3,bitmap.getHeight()/3,true); 83 84 if(changeScale == null) 85 {return;} 86 87 //画像は保存しなくていいので削除 88 mediaFile.delete(); 89 90 byte[] bytes; 91 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 92 //成功率と画質の兼ね合いで30とする 93 changeScale.compress(Bitmap.CompressFormat.JPEG, 30, byteArrayOutputStream); 94 bytes = byteArrayOutputStream.toByteArray(); 95 byteArrayOutputStream.close(); 96 97 //送信 98 AsyncHttps.url_id = 0; 99 new AsyncHttps(SendPic.this).execute(bytes); 100 101 } 102 catch (IOException e) 103 { 104 e.printStackTrace(); 105 Log.d("失敗:",String.valueOf(e)); 106 } 107 } 108 109 } 110 //Uriから画像(Bitmap形式)を取得する 111 private Bitmap getBitmapFromUri(Uri uri) throws IOException 112 { 113 ParcelFileDescriptor parcelFileDescriptor = 114 getContentResolver().openFileDescriptor(uri,"r"); 115 FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); 116 Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); 117 parcelFileDescriptor.close(); 118 return image; 119 } 120} 121
AsyncHttps
1public class AsyncHttps extends AsyncTask<byte[], Void, String> 2{ 3 private Context mContext; 4 public AsyncHttps(Context context) 5 {mContext = context;} 6 7 public static String res=null; 8 public static StringBuilder stblX = new StringBuilder(); 9 10 // 非同期処理 11 @Override 12 protected String doInBackground(byte[]... params) 13 { 14 String urlSt = "https://example/pic_upload.php"; //組織の名前が入っているので便宜上のURLです 15 16 HttpURLConnection con = null; 17 final StringBuilder stbl = new StringBuilder(); 18 try 19 {this.registerClientCert(mContext );} 20 catch ( Exception e ) 21 {e.printStackTrace();} 22 23 try 24 { 25 // URL設定 26 URL url = new URL(urlSt); 27 // HttpURLConnection 28 con = (HttpsURLConnection) url.openConnection(); 29 // no Redirects 30 con.setInstanceFollowRedirects(false); 31 // データを書き込む 32 con.setDoOutput(true); 33 // 時間制限 34 con.setReadTimeout(10000); 35 con.setConnectTimeout(20000); 36 // 接続 37 con.connect(); 38 // データ送信処理 39 OutputStream out = null; 40 try 41 { 42 out = con.getOutputStream(); 43 byte[] word = params[0]; 44 out.write(word); 45 out.flush(); 46 } 47 catch (IOException e) 48 {e.printStackTrace();} 49 finally 50 { 51 if (out != null) 52 {out.close(); } 53 } 54 55 final int status = con.getResponseCode(); 56 if (status == HttpURLConnection.HTTP_OK) 57 { 58 //レスポンス処理(検索とか) 59 final InputStream in = con.getInputStream(); 60 String encoding = con.getContentEncoding(); 61 if(encoding == null) 62 {encoding="UTF-8";} 63 final InputStreamReader inReader = new InputStreamReader(in,encoding); 64 final BufferedReader bufReader = new BufferedReader(inReader); 65 66 while ((res = bufReader.readLine()) != null) 67 {stbl.append(res);} 68 bufReader.close(); 69 inReader.close(); 70 in.close(); 71 72 } 73 else 74 {Log.d("接続失敗", String.valueOf(status));} 75 } 76 catch (IOException e) 77 {Log.d("接続失敗", String.valueOf(e)); } 78 finally { 79 if (con != null) { 80 con.disconnect(); 81 } 82 } 83 return res; 84 } 85 //証明書の設定。通信するために必要 86 private void registerClientCert(Context context) throws Exception 87 { 88 KeyManagerFactory keyManagerFactory; 89 //証明書作成したときのパス 90 final char[] PASSWORD = "***PASSWORD***".toCharArray(); 91 InputStream inputStream; 92 try 93 { 94 inputStream = context.getResources().getAssets().open("certificate.pfx"); 95 KeyStore keyStore = KeyStore.getInstance("PKCS12"); 96 keyStore.load(inputStream,PASSWORD); 97 keyManagerFactory = KeyManagerFactory.getInstance("X509"); 98 keyManagerFactory.init(keyStore,PASSWORD); 99 SSLContext sslContext = SSLContext.getInstance("TLS"); 100 sslContext.init(keyManagerFactory.getKeyManagers(),null,null); 101 HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); 102 }catch (IOException e) 103 {throw new Exception(e);} 104 catch (NoSuchAlgorithmException e) 105 {throw new Exception(e);} 106 } 107}
pic_upload
1<?php 2 //受信したbody部分を取り出す 3 $bin = file_get_contents("php://input"); 4 $path = dirname(__FILE__); 5 6 //日本時間に設定(ファイル名用) 7 date_default_timezone_set('Asia/Tokyo'); 8 $now = microtime(true); 9 $ms = (int)($now - ((int)$now) * 1000); 10 $msStr = str_pad($ms, 3, 0, STR_PAD_LEFT); 11 $dtStr = date("Ymd_His"). "_" . substr(explode(".", (microtime(true) . ""))[1], 0, 3); 12 13 //ファイル名を設定 14 $fname = $path . "\". $dtStr . ".jpg"; 15 $picname = $dtStr . ".jpg"; 16 17 unlink($fname); 18 19 $fp = fopen($fname, 'wb'); 20 fwrite($fp, $bin); 21 fclose($fp); 22?> 23
試したこと
画像を分割し、複数回に分けて送信
→合体時に後に送信した画像が復元されず失敗
タイムアウトの時間を変更
→待ち時間最大まで使用するが、timeoutやconnect pearが発生し失敗
補足情報
AndroidStudioバージョン:3.1.4
スマホのAndroidバージョン:8.0.0
見よう見まねの拙いコードではございますが、
何卒宜しくお願い致します。
あなたの回答
tips
プレビュー