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

Android使用Retrofit上传PDF到PHP服务器遇FileNotFoundException

问题:使用Retrofit上传PDF时出现FileNotFoundException错误

我尝试将多个PDF文件上传到服务器,编写代码后执行时onFailure()持续报错:

java.io.FileNotFoundException: /document/document:1000011388: open failed: ENOENT (No such file or directory)

相关代码

server_interface.java

public interface server_interface {
    @Multipart
    @POST("server.php")
    Call<ResponseBody> uploadMultipleFilesDynamic(
        @Part List<MultipartBody.Part> files
    );
}

MainActivity.java(核心逻辑)

server_interface api;

@Override
protected void onCreate(Bundle savedInstanceState) {
    List<MultipartBody.Part> parts = new ArrayList<>();
    for(Uri uri: list){
        parts.add(prepareFilePart(uri));
    }
    OkHttpClient.Builder httpClient = new OkHttpClient.Builder()
                .callTimeout(2, TimeUnit.MINUTES)
                .connectTimeout(2, TimeUnit.MINUTES)
                .readTimeout(3, TimeUnit.MINUTES)
                .writeTimeout(3, TimeUnit.MINUTES);

    Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(server_interface.JSONURL)
                .addConverterFactory(ScalarsConverterFactory.create())
                .client(httpClient.build())
                .build();

    api = retrofit.create(server_interface.class);
    Call<ResponseBody> uploadcall = api.uploadMultipleFilesDynamic(parts);
    if(uploadcall != null){
        uploadcall.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
            if(response.body() != null) {
                if (response.isSuccessful()) {
                    Toast.makeText(MainActivity.this, response.body().toString(), Toast.LENGTH_SHORT).show();
                }
            }
            @Override
            public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
                Log.d("response",t.toString());
            }
        });
    }
}
@NonNull
private MultipartBody.Part prepareFilePart(Uri fileUri) {
    File file = new File(fileUri.toString());
    Log.d("exists", String.valueOf(file.exists())); //显示false
    RequestBody requestFile =
                RequestBody.create(
                        MediaType.parse(getContentResolver().getType(fileUri)),
                        file
                );
    return MultipartBody.Part.createFormData("pdf", file.getName(), requestFile);
}

server.php

<?php
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
    header('Content-Type: text/plain; charset=utf-8');
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: POST, GET, OPTIONS');

    if(count($_FILES) > 0){
        $location = array();
        $folder="attachments/";
        for($i=0; $i<count($_FILES['file']['name']); $i++){
            if(!file_exists($folder.$_FILES['file']['name'][$i])){
                $moved = move_uploaded_file($_FILES['file']['tmp_name'][$i],$folder.$_FILES['file']['name'][$i]);
                if($moved){
                    if($i != count($_FILES['file']['name'])-1)
                        array_push($location,$folder.$_FILES['file']['name'][$i].';');
                    else
                        array_push($location,$folder.$_FILES['file']['name'][$i]);
                }
                else{
                    array_push($location,"Unable to upload");
                }
           }
           else{
                if($i != count($_FILES['file']['name'])-1)
                    array_push($location,$folder.$_FILES['file']['name'][$i].';');
                else
                    array_push($location,$folder.$_FILES['file']['name'][$i]);
           }
        }
        if(count($location) != 0){
           if(!in_array("Unable to upload",$location))
               echo json_encode($location);
           else
               echo "An attachment wasn't uploaded. Please try again. If issue persists, contact ITC/NFR/MLG";
        }
        else
           echo "Unable to upload";
    }
?>

文件Uri获取代码

ActivityResultLauncher<Intent> intentresultlauncher = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
        result -> {
            if (result.getResultCode() == Activity.RESULT_OK) {
                Intent data = result.getData();
                if(data != null){
                    Uri pdf_data = data.getData();
                    prepareFilePart(pdf_data);
                }
            }
        }
);
button.setOnClickListener(view -> {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intentresultlauncher.launch(intent);
});

解决方案

问题根源

通过Intent.ACTION_OPEN_DOCUMENT获取的Uri是内容提供者(Content Provider)的访问Uri,并非真实文件路径,直接用new File(fileUri.toString())无法定位到文件,导致报错。

修复步骤

1. 修改文件读取逻辑,通过ContentResolver获取文件内容

替换prepareFilePart方法,不要直接创建File对象,而是通过内容解析器读取文件字节流:

@NonNull
private MultipartBody.Part prepareFilePart(Uri fileUri) {
    String fileName = getFileName(fileUri);
    
    try {
        InputStream inputStream = getContentResolver().openInputStream(fileUri);
        byte[] fileBytes = IOUtils.toByteArray(inputStream);
        
        RequestBody requestFile = RequestBody.create(
            MediaType.parse(getContentResolver().getType(fileUri)),
            fileBytes
        );
        
        return MultipartBody.Part.createFormData("pdf", fileName, requestFile);
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

// 从Uri中提取文件名的辅助方法
private String getFileName(Uri uri) {
    String result = null;
    if (uri.getScheme().equals("content")) {
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {
                result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
    if (result == null) {
        result = uri.getPath();
        int cut = result.lastIndexOf('/');
        if (cut != -1) {
            result = result.substring(cut + 1);
        }
    }
    return result;
}

需要在build.gradle中添加依赖以使用IOUtils:

implementation 'commons-io:commons-io:2.11.0'

2. 修正服务器端参数名

客户端使用createFormData("pdf", fileName, requestFile),服务器端需对应读取$_FILES['pdf'],修改server.php的循环部分:

for($i=0; $i<count($_FILES['pdf']['name']); $i++){
    if(!file_exists($folder.$_FILES['pdf']['name'][$i])){
        $moved = move_uploaded_file($_FILES['pdf']['tmp_name'][$i],$folder.$_FILES['pdf']['name'][$i]);
        if($moved){
            if($i != count($_FILES['pdf']['name'])-1)
                array_push($location,$folder.$_FILES['pdf']['name'][$i].';');
            else
                array_push($location,$folder.$_FILES['pdf']['name'][$i]);
        }
        else{
            array_push($location,"Unable to upload");
        }
   }
   else{
        if($i != count($_FILES['pdf']['name'])-1)
            array_push($location,$folder.$_FILES['pdf']['name'][$i].';');
        else
            array_push($location,$folder.$_FILES['pdf']['name'][$i]);
   }
}

3. 确保Uri读取权限(可选)

Android 10及以上无需额外存储权限,但可以在获取Uri后添加持久权限,避免后续访问失效:

getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:15:07