📤 Upload & Download

KNET oferuje wygodne API do uploadu i downloadu plików z progress tracking.

Upload pliku

import rip.nerd.kitsunenet.upload.KNETUploader

val uploader = KNETUploader(client)

// Prosty upload
val response = uploader.upload(
    url = "https://api.example.com/upload",
    file = File("/path/to/file.jpg"),
    fieldName = "image"
)

// Z progress
uploader.uploadWithProgress(
    url = "https://api.example.com/upload",
    file = file,
    onProgress = { sent, total ->
        val percent = (sent * 100 / total).toInt()
        updateProgressBar(percent)
    }
)

Multipart upload

val response = uploader.multipart(
    url = "https://api.example.com/upload",
    parts = mapOf(
        "title" to "My Photo",
        "description" to "Vacation photo",
        "tags" to "summer,beach"
    ),
    files = mapOf(
        "image" to file,
        "thumbnail" to thumbnailFile
    )
)

// Z progress dla każdego pliku
uploader.multipartWithProgress(
    url = url,
    parts = metadata,
    files = files,
    onProgress = { fileName, sent, total ->
        Log.d("Upload", "$fileName: $sent/$total")
    }
)

Upload z ByteArray

// Z ByteArray
val imageBytes = bitmap.toByteArray()
val response = uploader.upload(
    url = url,
    data = imageBytes,
    fileName = "image.jpg",
    mimeType = "image/jpeg"
)

// Z InputStream
val inputStream = contentResolver.openInputStream(uri)!!
val response = uploader.upload(
    url = url,
    inputStream = inputStream,
    fileName = "document.pdf",
    mimeType = "application/pdf"
)

Download pliku

import rip.nerd.kitsunenet.download.KNETDownloader

val downloader = KNETDownloader(client)

// Prosty download
val file = downloader.download(
    url = "https://example.com/file.zip",
    destination = File(context.cacheDir, "file.zip")
)

// Z progress
downloader.downloadWithProgress(
    url = url,
    destination = destinationFile,
    onProgress = { downloaded, total ->
        val percent = if (total > 0) (downloaded * 100 / total).toInt() else -1
        updateProgress(percent)
    }
)

Resumable download

val downloader = KNETDownloader(client)

// Wspiera Range requests
val result = downloader.downloadResumable(
    url = url,
    destination = file,
    onProgress = { downloaded, total, resumed ->
        if (resumed) {
            Log.d("Download", "Resumed from $downloaded")
        }
        updateProgress(downloaded, total)
    }
)

// Jeśli połączenie zostało przerwane, następne wywołanie
// kontynuuje od miejsca przerwania

Download z auto-retry

val result = downloader.downloadWithRetry(
    url = url,
    destination = file,
    maxRetries = 3,
    onRetry = { attempt, error ->
        Log.w("Download", "Retry $attempt: ${error.message}")
    },
    onProgress = { downloaded, total ->
        updateProgress(downloaded, total)
    }
)

Batch download

val files = listOf(
    "https://example.com/file1.jpg" to File(cacheDir, "file1.jpg"),
    "https://example.com/file2.jpg" to File(cacheDir, "file2.jpg"),
    "https://example.com/file3.jpg" to File(cacheDir, "file3.jpg")
)

val results = downloader.downloadAll(
    files = files,
    maxConcurrent = 3,
    onProgress = { completed, total ->
        Log.d("Batch", "Downloaded $completed/$total files")
    }
)

results.forEach { (url, result) ->
    when (result) {
        is DownloadResult.Success -> Log.d("Download", "OK: $url")
        is DownloadResult.Error -> Log.e("Download", "Failed: $url - ${result.error}")
    }
}

Download to memory

// Download jako ByteArray (małe pliki)
val bytes = downloader.downloadToMemory(url)
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)

// Download jako String
val text = downloader.downloadToString(url)
println(text)

Upload/Download ViewModel

class FileTransferViewModel : ViewModel() {

    private val uploader = KNETUploader(client)
    private val downloader = KNETDownloader(client)

    private val _uploadProgress = MutableStateFlow(0f)
    val uploadProgress = _uploadProgress.asStateFlow()

    private val _downloadProgress = MutableStateFlow(0f)
    val downloadProgress = _downloadProgress.asStateFlow()

    fun uploadFile(file: File) {
        viewModelScope.launch {
            _uploadProgress.value = 0f

            try {
                uploader.uploadWithProgress(
                    url = "$baseUrl/upload",
                    file = file,
                    onProgress = { sent, total ->
                        _uploadProgress.value = sent.toFloat() / total
                    }
                )
                showSuccess("Plik wysłany!")
            } catch (e: Exception) {
                showError("Błąd uploadu: ${e.message}")
            }
        }
    }

    fun downloadFile(url: String, destination: File) {
        viewModelScope.launch {
            _downloadProgress.value = 0f

            try {
                downloader.downloadWithProgress(
                    url = url,
                    destination = destination,
                    onProgress = { downloaded, total ->
                        _downloadProgress.value = if (total > 0) {
                            downloaded.toFloat() / total
                        } else 0f
                    }
                )
                showSuccess("Plik pobrany!")
            } catch (e: Exception) {
                showError("Błąd downloadu: ${e.message}")
            }
        }
    }
}

Content URI (Android)

// Upload z Content URI (np. z galerii)
fun uploadFromUri(uri: Uri) {
    val contentResolver = context.contentResolver

    // Pobierz dane
    val inputStream = contentResolver.openInputStream(uri)!!
    val mimeType = contentResolver.getType(uri) ?: "application/octet-stream"
    val fileName = getFileNameFromUri(uri) ?: "file"

    uploader.upload(
        url = uploadUrl,
        inputStream = inputStream,
        fileName = fileName,
        mimeType = mimeType
    )
}

private fun getFileNameFromUri(uri: Uri): String? {
    return contentResolver.query(uri, null, null, null, null)?.use { cursor ->
        val index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
        cursor.moveToFirst()
        cursor.getString(index)
    }
}

📚 Zobacz też