初めに
GoでWebServerを立てて、Androidアプリからファイルのアップロードを行う
環境
PC Windows10
Android Studio 4.0
Kotlin 1.3.72
Android端末 Emulator(API Level 29)
Go 1.11.1
構成図
Androidアプリからファイルを送信して、サーバー側で保存、サーバーからアプリにステータスコードを返却する
実装
ローカルサーバー
package controllers
import (
"fmt"
"net/http"
"io/ioutil"
"os"
)
func apiSampleHandler(w http.ResponseWriter, r *http.Request) {
switch(r.Method) {
case "POST":
fmt.Println("POST")
// アップロードしたファイルとヘッダーを取得
file, fileHeader, _ := r.FormFile ("upload_file")
fileName := fileHeader.Filename // ヘッダーからファイル名を取得
defer file.Close() // ファイルを最後にClose
data, _ := ioutil.ReadAll(file) // ファイルを読み出し
saveFile, _ := os.Create(fileName) // 保存用のファイルを生成
defer saveFile.Close() // 保存用のファイルを最後にClose
_, err = saveFile.Write(data) // ファイルに書き込み
if err != nil {
fmt.Println("can not write file")
w.WriteHeader(http.StatusBadRequest)
return
}
case "GET":
fmt.Println("GET")
}
// 200:OKを返す
w.WriteHeader(http.StatusOK)
}
// main.goから呼び出してサーバーを起動
func StartWebServer() error {
http.HandleFunc("/", apiSampleHandler)
return http.ListenAndServe(":8080", nil)
}
main.goを実行し、サーバーを起動
http://localhost:8080 にアクセスして、サーバーが起動していることを確認
Android アプリ
1.Androidmanifest.xml
インターネットにアクセスするためのPermissionを定義
<uses-permission android:name="android.permission.INTERNET" />
Android 9.0からhttpの通信の設定がOFFになるのでusesCleartextTrafic
をtrueに設定
<application
省略
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity">
省略
</activity>
</application>
2.ファイルを生成
Android端末のアプリ固有領域(/data/data/{package_name}/files
配下)にテキストファイルを生成
const val FILE_EXPAND = ".txt"
class File {
fun makeTxtFile(context: Context, fileName: String, str: String) {
try {
context.openFileOutput(fileName + FILE_EXPAND, Context.MODE_PRIVATE).use {
it.write(str.toByteArray())
}
} catch (e: IOException) {
Log.e("File", "#makeTxtFile $e")
}
}
}
ファイルの生成を確認
Device File Explorer でデバイス上のファイルを表示する
3.サーバーアクセス
ファイルを読み込んで、http通信でファイルを送信する
fun startConnection(context: Context, requestUrl: String, requestMethod: String, fileName: String): Pair<Int, String> {
val url = URL(requestUrl) // URLオブジェクト生成
// UrlConnectionオブジェクト生成
val urlConnection = url.openConnection() as HttpURLConnection
var result = ""
var responseCode = 0
try {
val boundary = "--------------------------"
urlConnection.requestMethod = requestMethod // POST, GETなど
urlConnection.doOutput = true // リクエストボディの送信
urlConnection.doInput = true // レスポンスボディの受信
urlConnection.useCaches = false // キャッシュの利用
// multipart/form-data:複数のデータを送信、boundary:複数データ間の区切り
urlConnection.setRequestProperty(
"Content-Type",
"multipart/form-data; boundary=$boundary"
)
val filePath = context.filesDir // アプリ固有領域のパス
val file = File("$filePath/$fileName$FILE_EXPAND") // ファイルオブジェクトを生成
FileInputStream(file).use { fileInputStream ->
urlConnection.connect() // コネクションを確立
DataOutputStream(urlConnection.outputStream).use {
it.writeBytes( // headerを設定
TWO_HYPHEN + boundary + LINE_END +
"Content-Disposition: form-data; name=\"upload_file\"; " +
"filename=\"$fileName$FILE_EXPAND\"$LINE_END" +
"Content-Type: application/octet-stream$LINE_END$LINE_END"
)
// ファイル書き込み
val buffer = ByteArray(BUFFER_SIZE)
var bytesRead: Int
do {
bytesRead = fileInputStream.read(buffer)
if (bytesRead == -1) break
it.write(buffer, 0, bytesRead)
} while (true)
it.writeBytes( // footerの設定
LINE_END + TWO_HYPHEN + boundary + TWO_HYPHEN + LINE_END
)
it.flush()
}
}
responseCode = urlConnection.responseCode // レスポンスコードを取得
BufferedReader(InputStreamReader(urlConnection.inputStream)).use {
val sb = StringBuffer()
for (line in it.readLines()) {
line.let { sb.append(line) }
}
result = sb.toString()
}
} catch (e: Exception) {
Log.e("Net", "#startConnection$e")
} finally {
urlConnection.disconnect()
}
return responseCode to result
}
}
4.IPアドレスの確認
アクセスしたいサーバー(PC)のIPアドレスをcommand promptで以下を実行し確認
>ipconfig
5.PCとAndroid端末を同一のネットワーク環境に接続
6.Androidにて接続
Net#startConnectionのrequestUrlにhttp://{確認したIPアドレス}:8080を設定してアプリを実行
ファイルのアップロードを確認
PCにてmain.goと同じディレクトリを確認する
ファイルが作成されていれば完了
最後に
全体ソースはこちら
アクセス先やRequestMethod、ファイル名はアプリで動的に変更できるようにしています。
参考文献
Goサーバー側
Androidアプリ側
[Android] アプリ内にファイルを保存する FileOutputStream, FileInputStream
Upload File To Server - Android Example
[Android]kotlinでのwhile-FileInputStream.readエラー
【Kotlin】複数の値を返したい!