You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Forms无法将图片以Base64字符串上传至Cosmos DB(SDK3)

Xamarin Forms上传Base64头像到Azure Cosmos DB超时问题

我在学习Xamarin Forms,尝试将裁剪后的头像转为Base64字符串上传到Azure Cosmos DB时,出现RequestTimeout错误。APP内可以正常显示裁剪后的图片,但上传操作失败,错误日志如下:

DocDBTrace Warning: 0 : RNTBD call timed out on channel xxx.xxx.x.xx:xxxx -> xx.xx.xx.x:xxxxx. Error: SendTimeout
DocDBTrace Error: 0 : DocumentClientException with status code RequestTimeout, message: Message: Request timed out. More info: https://aka.ms/cosmosdb-tsg-request-timeout, inner exception: Microsoft.Azure.Documents.TransportException: A client transport error occurred: Sending the request timed out. (Time: 2023-01-14T19:10:07.2121350Z, activity ID: 38c1b0d0-847e-4eff-bb41-dfc541489b24, error code: SendTimeout [0x000E], base error: HRESULT 0x80131500, URI: rntbd://cdb-ms-prod-xxxxpartitions/xxxxx/replicas/xxxxx/, connection: xxx.xxx.x.xx:xxxxx -> xx.xx.xx.x:xxxxx, payload sent: True)
  at Microsoft.Azure.Documents.Rntbd.Channel.RequestAsync (Microsoft.Azure.Documents.DocumentServiceRequest request, Microsoft.Azure.Documents.TransportAddressUri physicalAddress, Microsoft.Azure.Documents.ResourceOperation resourceOperation, System.Guid activityId, Microsoft.Azure.Documents.TransportRequestStats transportRequestStats) [0x0035a] in <ffe3c00d433342948947b1c93a852e2f>:0 
  at Microsoft.Azure.Documents.Rntbd.LoadBalancingPartition.RequestAsync (Microsoft.Azure.Documents.DocumentServiceRequest request, Microsoft.Azure.Documents.TransportAddressUri physicalAddress, Microsoft.Azure.Documents.ResourceOperation resourceOperation, System.Guid activityId, Microsoft.Azure.Documents.TransportRequestStats transportRequestStats) [0x001ad] in <ffe3c00d433342948947b1c93a852e2f>:0 
  at Microsoft.Azure.Documents.Rntbd.TransportClient.InvokeStoreAsync (Microsoft.Azure.Documents.TransportAddressUri physicalAddress, Microsoft.Azure.Documents.ResourceOperation resourceOperation, Microsoft.Azure.Documents.DocumentServiceRequest request) [0x0015c] in <ffe3c00d433342948947b1c93a852e2f>:0 , and response headers: null
DocDBTrace Warning: 0 : ClientRetryPolicy: RequestTimeout. Failed Location: https://xxxxxx.documents.azure.com/; ResourceAddress: dbs/xxxxx/colls/xxxx/docs/6957c0bb-e3cb-4cc3-8b04-3bbef4b85c37
DocDBTrace Error: 0 : Operation will NOT be retried. Current attempt 0, Status Code: RequestTimeout 

实现流程

  • 使用ImageCropper.Forms库裁剪图片
  • 将图片转为流后转成Base64字符串
  • 调用Cosmos DB的ReplaceItemAsync接口更新用户数据

相关代码

图片读取与上传代码

var result = await MediaPicker.PickPhotoAsync(new MediaPickerOptions {});

if (result != null)
{
    new ImageCropper()
    {
        CropShape = ImageCropper.CropShapeType.Oval,
        AspectRatioX = 1,
        AspectRatioY = 1,
        Success = (imageFile) =>
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                Stream stream = File.OpenRead(imageFile);
                byte[] bytes;
                using (var memoryStream = new MemoryStream())
                {
                    stream.CopyTo(memoryStream);
                    bytes = memoryStream.ToArray();
                    UserConnected.UserImage = MySharedAppClasses.BitmapToBase64(bytes);
                    
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        ProfileImage.Source = ImageSource.FromStream(() => new MemoryStream(MySharedAppClasses.Base64ToBitmap(UserConnected.UserImage)));
                    });
                    
                    // 上传头像
                    MySharedAppClasses.UpdateUserInfos(UserConnected);
                }
            });
        }
    }.Show(this, result.FullPath);
}

Cosmos DB上传函数

public async static Task<MyUser> UpdateUserAsync(MyUser myUser)
{
    try          
    {
        ItemResponse<MyUser> response = await container.ReplaceItemAsync(myUser, myUser.MyId.ToString());
        return response.Resource;
    }
    catch (Exception e)
    {
        ErrorManager.InsertErrorAsync("update user error", e.Message);
        return null;
    }
}

实体类定义

public class MyUser
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }

    [JsonProperty(PropertyName = "id")]
    public Guid MyId { get; set; }

    [JsonProperty(PropertyName = "username")]
    public string Username { get; set; }

    [JsonProperty(PropertyName = "userimage")]
    public string UserImage { get; set; }
}

解决方法

1. 压缩Base64图片大小

Base64编码会让图片体积增加约33%,如果裁剪后的图片仍然较大(比如超过1MB),会导致请求payload过大触发超时。可以在转Base64前对图片进行压缩:

// 依赖SkiaSharp NuGet包实现图片压缩
public static byte[] CompressImage(byte[] imageBytes, int quality = 50)
{
    using (var inputStream = new MemoryStream(imageBytes))
    using (var outputStream = new MemoryStream())
    {
        var image = SKBitmap.Decode(inputStream);
        var encodedImage = image.Encode(SKEncodedImageFormat.Jpeg, quality);
        encodedImage.SaveTo(outputStream);
        return outputStream.ToArray();
    }
}

替换原有代码中的bytes = memoryStream.ToArray():

bytes = CompressImage(memoryStream.ToArray(), 50); // 质量值范围0-100,值越小压缩率越高

2. 调整Cosmos DB客户端超时设置

默认客户端超时可能不足以处理较大payload,初始化客户端时延长超时时间:

var client = new CosmosClient(connectionString, new CosmosClientOptions
{
    RequestTimeout = TimeSpan.FromSeconds(30),
    MaxRetryAttemptsOnRateLimitedRequests = 5,
    MaxRetryWaitTimeOnRateLimitedRequests = TimeSpan.FromSeconds(10)
});

3. 避免在主线程执行网络操作

当前代码在主线程中直接调用上传接口,会阻塞主线程导致超时,应放到后台线程执行:

// 替换原有的上传代码
Task.Run(async () =>
{
    await MySharedAppClasses.UpdateUserInfos(UserConnected);
});

4. 检查Cosmos DB配置

  • 临时提高集合的吞吐量(RU/s)测试,排查是否是资源不足导致的超时
  • 检查分区键设计,避免单分区负载过高引发热点问题

5. 启用重试策略

日志显示请求未触发重试,配置客户端重试策略应对临时网络波动:

var clientOptions = new CosmosClientOptions
{
    RetryOptions = new CosmosRetryOptions
    {
        MaxRetryAttemptsOnThrottledRequests = 5,
        MaxRetryWaitTime = TimeSpan.FromSeconds(10)
    }
};

内容的提问来源于stack exchange,提问作者hugo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 02:55:18