Файловый сервер с использованием Ktor и локальной точки доступа на android - PullRequest
0 голосов
/ 11 июля 2020

Я пытаюсь создать сервер внутри приложения android. Идея состоит в том, чтобы создать локальную точку доступа из самого телефона и запустить HTTP-сервер. Покажите пользователям инструкции по подключению к указанной точке доступа. Затем URL-адрес, который при посещении автоматически загружает файл с телефона.

Серверный поток

class Server(private val filePath: File): Thread() {
    override fun run() {
        val file = File(filePath, "test.txt")
        file.writeText("testing file")
        val server = embeddedServer(Netty, 8080) {
            routing {
                get("/") {
                    call.respondText { "Works" }
                }
                get("/files") {
                    call.respondFile(file, configure = {
                        this.setProperty(AttributeKey("Content-Disposition"), "attachment; filename=\"test.txt\"")
                    })
                }
            }
        }
        server.start(wait = true)
    }
}

Локальная настройка Hotstop

        val mWifiManager = baseContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?
        mWifiManager?.startLocalOnlyHotspot(object : WifiManager.LocalOnlyHotspotCallback() {
            override fun onStarted(reservation: WifiManager.LocalOnlyHotspotReservation?) {
                super.onStarted(reservation)
                val wifiConfiguration = reservation?.wifiConfiguration
                if (wifiConfiguration != null) {
                    Log.d("testing", wifiConfiguration.preSharedKey)
                }
            }

            override fun onStopped() {
                Toast.makeText(baseContext, "STOP", Toast.LENGTH_LONG).show()
                super.onStopped()
            }

            override fun onFailed(reason: Int) {
                Toast.makeText(baseContext, "FAIL", Toast.LENGTH_LONG).show()
                super.onFailed(reason)
            }
        }, Handler())

gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29

    defaultConfig {
        applicationId "com.extermeprogramming.servertest"
        minSdkVersion 26
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    packagingOptions {
        pickFirst "META-INF/io.netty.versions.properties"
        exclude 'META-INF/INDEX.LIST'
    }
    
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    // Ktor, HTTP SERVER
    ext.ktor_version = '1.3.2'
    implementation "io.ktor:ktor-server-core:$ktor_version"
    implementation "io.ktor:ktor-server-netty:$ktor_version"
    implementation "org.slf4j:slf4j-simple:1.7.9"

}

AndroidManifes

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.extermeprogramming.servertest" >

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.extermeprogramming.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/paths" />
        </provider>

        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Когда сервер и точка доступа включаются в первый раз, запрос URL-адреса 192.169.43.1:8080/files завершается ошибкой, и точка доступа выключается без вызова каких-либо обратных вызовов ( onStopped, onFailed). Когда точка доступа снова включена, все работает, как ожидалось, и загрузка файла происходит без проблем, а точка доступа остается включенной.

Единственная ошибка, выдаваемая Ktor, - Call execution has been cancelled. Изменение порядка шагов, сначала включение точки доступа, а затем сервера, приводит к тому же исключению. Я думаю, что есть некоторая конфигурация либо в Ktor, либо в Hotspot, которая исправит эту проблему, потому что после исключения, когда Hotspot снова включается, все работает.

...