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

Android端通过REST API向Frappe上传文件遇内部服务器错误求助

问题:Android通过URI上传文件到Frappe REST API报INTERNAL SERVER ERROR

通过URL调用Frappe的REST API上传文件正常,但从Android设备用URI上传时触发INTERNAL SERVER ERROR,相关代码和日志如下:


Android端相关代码

AsyncTask中调用上传逻辑

//Call upload function from async task
@Override
protected String doInBackground(String... strings) {

        if (strings[0] != null) {
            try {
                file1 = new File(strings[0]);
             
                RequestBody requestFile =
                        RequestBody.create(
                                MediaType.parse(getContentResolver().getType(uri)),
                                file1
                        );

                // MultipartBody.Part is used to send also the actual file name
                MultipartBody.Part body =
                        MultipartBody.Part.createFormData("file_url", file1.getName(), requestFile);

                uploadFile(body);

            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try {
                file2 = new File(strings[1]);
               
                // create RequestBody instance from file
                RequestBody requestFile =
                        RequestBody.create(
                                MediaType.parse(getContentResolver().getType(uri)),
                                file2
                        );

                // MultipartBody.Part is used to send also the actual file name
                MultipartBody.Part body =
                        MultipartBody.Part.createFormData("file_url", file2.getName(), requestFile);
                uploadFile(body);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
}

Retrofit上传方法

//Retrofit Call

private void uploadFile(MultipartBody.Part body){
        Log.v("HitMan",body.toString());
        String doctypeString = "Lead";
        String docnameString = name;
        RequestBody doctype =
                RequestBody.create(
                        okhttp3.MultipartBody.FORM, doctypeString);
        RequestBody docname =
                RequestBody.create(
                        okhttp3.MultipartBody.FORM, docnameString);
        Log.v("Body",body.toString());
        Log.v("Doctype",doctype.toString());
        Log.v("Docname",docname.toString());

        UserService uploadService = retrofit.create(UserService.class);

        Call<ResponseBody> call4 = uploadService.uploadFile(body,doctype,docname);
        call4.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if(response.isSuccessful()) {
                    progressBar.setVisibility(View.GONE);
                    Toast.makeText(LeadDetailActivity.this,"Uploaded Successfully",Toast.LENGTH_SHORT).show();
                }
                else{
                    progressBar.setVisibility(View.GONE);
                    Log.v("Custom Error :",response.message());
                    Toast.makeText(LeadDetailActivity.this,"Process Failed!",Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                progressBar.setVisibility(View.GONE);
                Log.v("Create Response Error", t.toString());
            }
        });
    }

Retrofit接口定义

//Interface

@Multipart
@POST("api/method/upload_file")
Call<ResponseBody> uploadFile(@Part MultipartBody.Part file_url,@Part("doctype") RequestBody doctype,@Part("docname") RequestBody docname);

错误日志

V/URI: content://com.mi.android.globalFileexplorer.myprovider/external_files/temp.jpg
D/File Path :: /storage/emulated/0/temp.jpg
V/FA: Activity resumed, time: 5674872
V/FA: Connecting to remote service
D/FA: Connected to remote service
V/FA: Processing queued up service tasks: 1
V/HitMan: okhttp3.MultipartBody$Part@7a76c54
V/Body: okhttp3.MultipartBody$Part@7a76c54
V/Doctype: okhttp3.RequestBody$2@242e7fd
V/Docname: okhttp3.RequestBody$2@3da0cf2
V/Custom Error :: INTERNAL SERVER ERROR

排查方向与解决方案

1. 修正文件参数名

Frappe的upload_file接口中,本地文件上传的表单字段名是file,而file_url是用来上传网络文件地址的参数。你当前代码里用了错误的字段名,导致服务端无法识别上传的文件:

  • 将MultipartBody.Part.createFormData("file_url", ...)改为MultipartBody.Part.createFormData("file", ...)
  • 同时把接口定义中的@Part MultipartBody.Part file_url改为@Part MultipartBody.Part file

2. 解决Content URI的权限与读取问题

直接将content://开头的URI转成File对象存在权限风险,部分设备的文件管理器会限制APP直接读取对应路径。应该通过ContentResolver读取文件流来创建RequestBody:

// 替换原有的File转RequestBody逻辑
InputStream inputStream = getContentResolver().openInputStream(uri);
byte[] fileBytes = IOUtils.toByteArray(inputStream); // 可引入org.apache.commons.io.IOUtils,或自行实现流转字节数组
RequestBody requestFile = RequestBody.create(
    MediaType.parse(getContentResolver().getType(uri)) != null ? 
        MediaType.parse(getContentResolver().getType(uri)) : 
        MediaType.parse("application/octet-stream"),
    fileBytes
);

3. 检查Frappe服务端日志

INTERNAL SERVER ERROR的具体原因需要看服务端日志,登录Frappe后台,查看Logs模块中的错误堆栈,能直接定位服务端解析失败的具体原因(比如参数缺失、文件格式不支持等)。

4. 确认MediaType正确性

如果getContentResolver().getType(uri)返回null,手动指定对应文件的MediaType(比如图片用image/jpeg、image/png),避免服务端因无法识别文件类型报错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 17:37:35