LoginSignup
0
2

More than 3 years have passed since last update.

[Golang]ローカルサーバーを立てた~ファイルアップロード~

Last updated at Posted at 2020-10-17

初めに

GoでWebServerを立てて、Androidアプリからファイルのアップロードを行う

環境

PC Windows10
Android Studio 4.0
Kotlin 1.3.72
Android端末 Emulator(API Level 29)
Go 1.11.1

構成図

go_server.png
Androidアプリからファイルを送信して、サーバー側で保存、サーバーからアプリにステータスコードを返却する

実装

ローカルサーバー

Server.go
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を定義

AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />

Android 9.0からhttpの通信の設定がOFFになるのでusesCleartextTraficをtrueに設定

AndroidManifest.xml
<application
     省略
    android:usesCleartextTraffic="true">
    <activity android:name=".MainActivity">
        省略
    </activity>
</application>

2.ファイルを生成

Android端末のアプリ固有領域(/data/data/{package_name}/files配下)にテキストファイルを生成

File.kt
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通信でファイルを送信する

Net.kt
 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サーバー側

Go言語でのファイルアップロード

Androidアプリ側

[Android] アプリ内にファイルを保存する FileOutputStream, FileInputStream
Upload File To Server - Android Example
[Android]kotlinでのwhile-FileInputStream.readエラー
【Kotlin】複数の値を返したい!

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2