使用 POST 将文件从 Android Kotlin 发送到 C#

Send file from Android Kotlin to C# using POST

我们有两个应用程序:一个 C# REST-API 和一个 Kotlin Android 应用程序,我们使用 Google Platform Cloud Bucket 来托管图像。

Android应用会上传图片,但C#REST-API需要上传到Google云平台

这是将文件上传到 Google Cloud Buckets 的工作 C# 代码:

        [HttpPost]
        [Route("upload")]
        public IActionResult Upload()
        {
            var storageClient = StorageClient.Create(google_credentials);

            string fileToUpload ="/Users/niel/Downloads/new_cat.jpg";
            
            using (var fileStream = new FileStream(fileToUpload, FileMode.Open,
                FileAccess.Read, FileShare.Read))
            {
                storageClient.UploadObject("test_storage_fotos", "new_cat", "image/jpeg", fileStream);
            }
            Console.WriteLine("uploaded the file successfully");

            return Ok();
        }
        

现在我需要用 POST 请求中的内容替换 fileToUpload。有没有办法做到这一点?图片来自 Android app > C# API > Google Buckets?从 C# API 到 Google Buckets 的 link 已经在工作了。

Kotlin 中有没有办法以某种方式获取图像的字节串,post 将其发送到我的 C# API,后者获取内容并将其放入 FileStream ?我可以使用 storageClient.UploadObject 上传 FileStream 吗?有这种可能吗?

谢谢!

是的,你绝对可以做到这一点。只需通过 http 协议将文件发送到服务器,内容类型为 multipart/form-data

在 kotlin 中,您可以使用 ktor 或任何其他 http 库来执行此操作。 对于 ktor,您需要添加一个实现依赖项

implementation "io.ktor:ktor-client-android:1.5.4"

您可能还需要在 AndroidManifest.xml

中添加额外的权限
<uses-permission android:name="android.permission.INTERNET" />

然后您可以发送包含此代码段的文件。请注意,imageUri 是一个内容 uri,对于文件 uri,代码会有点不同

private fun getFileName(resolver: ContentResolver, uri: Uri): String {
    val returnCursor: Cursor = resolver.query(uri, null, null, null, null)!!
    val nameIndex: Int = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
    returnCursor.moveToFirst()
    val name: String = returnCursor.getString(nameIndex)
    returnCursor.close()
    return name
}

suspend fun postAndImage(imageUri: Uri, uploadEndPoint: String) {
    val client = HttpClient(Android)

    val cr = applicationContext.contentResolver
    if(cr.getType(imageUri) == null) {
        //process error
        return
    }

    val stream = cr.openInputStream(imageUri)

    if(stream == null) {
        //process error
        return
    }

    val response: HttpResponse = client.submitFormWithBinaryData(
        url = uploadEndPoint,
        formData = formData {
            append("image", InputProvider { stream.asInput() }, Headers.build {
                append(HttpHeaders.ContentType, cr.getType(imageUri)!!)
                append(HttpHeaders.ContentDisposition, "filename=${getFileName(cr, imageUri)}")
            })
        }
    )

    stream.close()

    //process response
}

并且您需要稍微修改上传功能

[HttpPost]
[Route("upload")]
//the name of the argument must match the key that you pass in "append" function
public async Task<IActionResult> Post(IFormFile image)
{            
    var storageClient = StorageClient.Create(google_credentials);

    using (var stream = image.OpenReadStream())
    {           
        //it's also possible to get original file name from file name property
        var fileName = Guid.NewGuid() + "." + Path.GetExtension(image.FileName);
            
        //assuming bucket is already created
        var storageObject = await storageClient
                    .UploadObjectAsync("test_storage_fotos", fileName, "image/jpeg", stream);
        //save information about a storage object in database
    }

    return Ok();
}