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

.NET MAUI中RestSharp新版上传文件遇边界错误,求新版兼容方案

问题:新版RestSharp上传文件时出现「Start boundary not found」错误

我开发了一款对接第三方API的.NET MAUI应用,通过文件选择器获取文件的代码如下:

private async void file_Clicked(object sender, EventArgs e)
{
    var result = await FilePicker.PickAsync(new PickOptions
    {
        PickerTitle = "选择上传文件..."
    });

    if (result == null)
        return;

    var stream = await result.OpenReadAsync();

    searchAndReplaceViewModel.ReplaceFile = stream;
    searchAndReplaceViewModel.ReplaceFileName = result.FileName;
    file.Text = result.FileName;
    file.BackgroundColor = Colors.LightGreen;
    
}

ViewModel中保存文件流后调用上传的代码:

public object ReplaceFile { get; internal set; }

// 中间省略大量代码

[RelayCommand]
async Task UploadFile(dtoObjectsBase doc)
{
    if (IsLoading) return;
    try
    {
        if(App.SearchAndReplaceService.UploadFile(doc, (BufferedStream)ReplaceFile, ReplaceFileName))
        {
            await Shell.Current.DisplayAlert("成功", "文件上传成功", "确定");
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"文件上传失败: {ex.Message}");
        await Shell.Current.DisplayAlert("错误", "文件上传出错", "确定");
    }
    finally
    {
        IsLoading = false;
    }
}

对应的RestSharp上传方法:

public Operation<FileUploadResponse> FileUpload(BufferedStream file, Guid guid, string filename)
{
    Operation<FileUploadResponse> fileUpload = new();

    FileUpload fileUploadRequest = new()
    {
        Guid = guid,
        RequestStream = file.UnderlyingStream
    };

    try
    {
        var options = new RestClientOptions(Settings.Server)
        {
            MaxTimeout = -1,
        };
        var client = new RestClient(options);
        var request = new RestRequest(ApiPaths.FileUpload + "?guid=" + guid.ToString(), Method.Post);
        
        request.AddHeader("Content-Type", "multipart/form-data");
        //request.AddFile("MyfactoryImport", );
        byte[] data = new byte[file.Length];
        file.Read(data, 0, (int)file.Length);
        request.AlwaysMultipartFormData = true;
        //request.AddFile("RequestStream", data, RestSharp.ContentType.Binary);
        request.AddFile(filename, data, RestSharp.ContentType.Binary);


        fileUpload.Value = JsonConvert.DeserializeObject<FileUploadResponse>(client.Execute(request).Content);
        fileUpload.Success = true;
        
        //fileUpload.Value = APICall<FileUpload, FileUploadResponse>(fileUploadRequest, ApiPaths.FileUpload);
    }
    catch (Exception ex)
    {
        fileUpload.ErrorMsg = "API请求出错: " + ex.Message;
    }

    return fileUpload;
}

上传时RestSharp返回InternalServerError,API端日志显示:

HandleFileUpload (general) failed for guid 8f5ae2b3-39e0-41b8-bc18-b60c2b75fc26 | (null) | (ID:(null) System:(null))
System.ApplicationException: Cannot perform file upload. Start boundary not found.

用Postman测试上传正常,Postman生成的RestSharp代码如下:

var options = new RestClientOptions("")
{
  MaxTimeout = -1,
};
var client = new RestClient(options);
var request = new RestRequest("https://servername/instance/services/fileupload?guid=6e813e27-bee8-4359-a046-04437365c903", Method.Post);
request.AlwaysMultipartFormData = true;
request.AddFile("file", "dOLC4eFA4/temp.pdf");
RestResponse response = await client.ExecuteAsync(request);
Console.WriteLine(response.Content);

第三方API文档仅给出以下说明:

POST /server/services/fileupload HTTP/1.1 
Host: localhost 
Accept: application/json
Content-Type: application/json
Content-Length: length

{"Guid":"00000000000000000000000000000000"}

目前我通过降级到RestSharp 106.15.0解决了问题,但希望使用新版本,请问如何在新版RestSharp中解决这个边界错误?


解决方案

问题根源

新版RestSharp中,手动添加Content-Type: multipart/form-data头会覆盖框架自动生成的带边界标识的Content-Type,导致API无法识别请求边界,从而抛出「Start boundary not found」错误。

修复步骤

  1. 移除手动添加的Content-Type头
    新版RestSharp会在使用AddFile且设置AlwaysMultipartFormData = true时,自动生成包含正确边界的multipart/form-data请求头,无需手动添加。

  2. 使用正确的AddFile重载方法
    确保指定文件参数的名称(需和API期望的参数名一致,比如Postman中的file),而不是直接用文件名作为参数名。

  3. 优化文件流处理
    避免将流转换为字节数组后再上传,直接使用文件流可以减少内存占用,同时避免流位置错误的问题。

修复后的代码

修改FileUpload方法如下:

public async Task<Operation<FileUploadResponse>> FileUpload(Stream fileStream, Guid guid, string filename)
{
    Operation<FileUploadResponse> fileUpload = new();

    try
    {
        var options = new RestClientOptions(Settings.Server)
        {
            MaxTimeout = -1,
        };
        using var client = new RestClient(options);
        var request = new RestRequest($"{ApiPaths.FileUpload}?guid={guid}", Method.Post);
        
        // 移除手动添加的Content-Type头
        // request.AddHeader("Content-Type", "multipart/form-data");
        
        request.AlwaysMultipartFormData = true;
        // 使用API期望的参数名(比如"file",和Postman一致),传入文件流、文件名和内容类型
        request.AddFile("file", () => fileStream, filename, "application/octet-stream");

        var response = await client.ExecuteAsync(request);
        if (response.IsSuccessful)
        {
            fileUpload.Value = JsonConvert.DeserializeObject<FileUploadResponse>(response.Content);
            fileUpload.Success = true;
        }
        else
        {
            fileUpload.ErrorMsg = $"API请求失败: {response.StatusCode}";
        }
    }
    catch (Exception ex)
    {
        fileUpload.ErrorMsg = "API请求出错: " + ex.Message;
    }

    return fileUpload;
}

同时修改ViewModel中的调用,直接传入原始流(无需转换为BufferedStream):

[RelayCommand]
async Task UploadFile(dtoObjectsBase doc)
{
    if (IsLoading) return;
    try
    {
        var result = await App.SearchAndReplaceService.FileUpload((Stream)ReplaceFile, doc.Guid, ReplaceFileName);
        if(result.Success)
        {
            await Shell.Current.DisplayAlert("成功", "文件上传成功", "确定");
        }
        else
        {
            await Shell.Current.DisplayAlert("错误", result.ErrorMsg, "确定");
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"文件上传失败: {ex.Message}");
        await Shell.Current.DisplayAlert("错误", "文件上传出错", "确定");
    }
    finally
    {
        IsLoading = false;
    }
}

额外说明

  • 第三方API文档给出的示例是Content-Type: application/json,但实际Postman测试用的是multipart/form-data,说明API支持两种上传方式?但你的场景是上传文件,所以用multipart/form-data是正确的。
  • 确保文件流的位置在起始处(如果之前读取过流,需要调用fileStream.Seek(0, SeekOrigin.Begin)重置位置)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 20:50:25