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

Android通过PHP从MySQL获取长篇文本时解析失败的问题排查

长文本分块下载失败问题排查与解决方案

我来帮你拆解这个问题——你遇到的部分请求JSON解析失败,大概率是代码潜在bug和服务商限制共同作用的结果,咱们结合你的代码和场景一步步分析:

问题背景回顾

我在在线MySQL数据库中存储了书籍量级的长文本,需要通过PHP服务器在Android应用中下载。最初尝试单次请求获取全部文本时,出现了JSON解析错误:

E/JSON Parser: Error parsing data org.json.JSONException: End of input at character 0 of

推测是文本过大导致的,于是改成分块(每次1000字符)获取,单独在浏览器测试PHP文件是可行的,但Android端的AsyncTask执行时仅部分请求成功(比如前8次及随机几次),其余还是报相同的解析错误。服务器托管在1and1,想知道这是服务商限制、代码bug,还是有更合适的文本获取方法?


你当前使用的代码

1. JSONParser类

public class JSONParser {
    static InputStream is = null;
    static JSONObject jObj = null;
    static String json = "";
    // constructor
    public JSONParser() {}
    // function get json from url
    // by making HTTP POST or GET method
    public JSONObject makeHttpRequest(String url, String method, List<NameValuePair> params) {
        // Making HTTP request
        try {
            // check for request method
            if(method == "POST"){
                // request method is POST
                // defaultHttpClient
                DefaultHttpClient httpClient = new DefaultHttpClient();
                HttpPost httpPost = new HttpPost(url);
                httpPost.setEntity(new UrlEncodedFormEntity(params));
                HttpResponse httpResponse = httpClient.execute(httpPost);
                HttpEntity httpEntity = httpResponse.getEntity();
                is = httpEntity.getContent();
            }else if(method == "GET"){
                // request method is GET
                DefaultHttpClient httpClient = new DefaultHttpClient();
                String paramString = URLEncodedUtils.format(params, "utf-8");
                url += "?" + paramString;
                HttpGet httpGet = new HttpGet(url);
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                is = httpEntity.getContent();
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    is, "iso-8859-1"), 10000000);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            is.close();
            json = sb.toString();
        } catch (Exception e) {
            Log.e("Buffer Error", "Error converting result " + e.toString());
        }
        Log.i("json", "here");
        Log.i("json", json + " , ");
        // try parse the string to a JSON object
        try {
            jObj = new JSONObject(json);
        } catch (JSONException e) {
            Log.e("JSON Parser", "Error parsing data " + e.toString());
        }
        // return JSON String
        return jObj;
    }
}

2. Android端AsyncTask代码

protected Boolean doInBackground(String... params) {
    boolean suc = false;
    // Check for success tag
    int success;
    try {
        // Building Parameters
        List<NameValuePair> parametri = new ArrayList<NameValuePair>();
        parametri.add(new BasicNameValuePair(TAG_BOOKID, Integer.toString(id)));
        JSONObject json = jsonParser.makeHttpRequest(
                url_book_file_length, "GET", parametri);
        // check your log for json response
        Log.d("Single Book length", json.toString());
        // json success tag
        success = json.getInt(TAG_SUCCESS);
        if (success == 1) {
            // successfully received product details
            JSONArray productObj = json
                    .getJSONArray(TAG_BOOK);
            // JSON Array
            // get first product object from JSON Array
            JSONObject jbook = productObj.getJSONObject(0);
            int length = jbook.getInt(TAG_LENGTH);
            int current = 1;
            String file = "";
            suc = true;
            while (current <= length){
                parametri = new ArrayList<NameValuePair>();
                parametri.add(new BasicNameValuePair(TAG_BOOKID, Integer.toString(id)));
                parametri.add(new BasicNameValuePair(TAG_START, Integer.toString(current)));
                if (length-current>=LENGTH_MAX){
                    parametri.add(new BasicNameValuePair(TAG_LENGTH, Integer.toString(LENGTH_MAX)));
                    current+= LENGTH_MAX;
                } else {
                    parametri.add(new BasicNameValuePair(TAG_LENGTH, Integer.toString(length-current+1)));
                    current = length+1;
                }
                json = jsonParser.makeHttpRequest(
                        url_book_file, "GET", parametri);
                // check your log for json response
                Log.d("Single Book pezzo file", "current " + Integer.toString(current) + " " + json.toString());
                // json success tag
                success = json.getInt(TAG_SUCCESS);
                if (success == 1) {
                    // successfully received product details
                    productObj = json
                            .getJSONArray(TAG_BOOK);
                    // JSON Array
                    // get first product object from JSON Array
                    jbook = productObj.getJSONObject(0);
                    file += jbook.getString(TAG_FILE);
                }else{
                    suc = false;
                    break;
                    // product with pid not found
                }
            }
            book.setFile(file);
            Log.i("File", "saved");
        }else{
            // product with pid not found
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return suc;
}

3. 对应的PHP文件代码

<?php
// array for JSON response
$response = array();
// include db connect class
require_once __DIR__ . '/db_connect.php';
// connecting to db
$db = new DB_CONNECT();
// check for post data
if ((isset($_GET["bookid"]))&&(isset($_GET["start"]))&&(isset($_GET["length"]))) {
    $start = $_GET['start'];
    $length = $_GET['length'];
    $bookid = $_GET['bookid'];
    // get a book from books table
    $result = mysql_query("SELECT SUBSTRING(file, $start, $length) AS file FROM book WHERE bookid = $bookid");
    if (!empty($result)) {
        // check for empty result
        if (mysql_num_rows($result) > 0) {
            $result = mysql_fetch_array($result);
            $book = array();
            $book["file"] = $result["file"];
            // success
            $response["success"] = 1;
            // user node
            $response["book"] = array();
            array_push($response["book"], $book);
            // echoing JSON response
            echo json_encode($response);
        } else {
            // no book found
            $response["success"] = 0;
            $response["message"] = "No book found";
            // echo no users JSON
            echo json_encode($response);
        }
    } else {
        // no book found
        $response["success"] = 0;
        $response["message"] = "No book found";
        // echo no users JSON
        echo json_encode($response);
    }
} else {
    // required field is missing
    $response["success"] = 0;
    $response["message"] = "Required field(s) is missing";
    //echoing JSON response
    echo json_encode($response);
}
?>

问题排查与解决方案

1. 代码层面的核心bug

(1)请求方式判断错误

在JSONParser的makeHttpRequest方法中,你用method == "POST"和method == "GET"做判断——这在Java中是引用比较,不是内容比较!当传入的method字符串是不同对象时,判断会失效,导致请求逻辑错误,无法获取到输入流,最终返回空JSON触发解析错误。

修复方式:改成内容比较:

if("POST".equals(method)){
    // POST逻辑
}else if("GET".equals(method)){
    // GET逻辑
}

(2)字符编码不匹配

你的JSONParser用iso-8859-1编码读取流,但json_encode默认输出UTF-8编码的JSON,编码不匹配会导致部分字符解析错误,甚至截断JSON内容。

修复方式:把InputStreamReader的编码改成utf-8:

BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"), 10000000);

(3)HttpClient频繁创建导致连接耗尽

你每次请求都新建DefaultHttpClient,短时间内发起大量分块请求时,会频繁创建销毁连接,可能触发客户端连接池耗尽或服务器连接限制,导致部分请求无响应。

修复方式:复用HttpClient实例,比如把DefaultHttpClient作为JSONParser的成员变量初始化一次:

public class JSONParser {
    static InputStream is = null;
    static JSONObject jObj = null;
    static String json = "";
    private DefaultHttpClient httpClient;

    public JSONParser() {
        httpClient = new DefaultHttpClient();
    }

    // 后续请求直接复用这个httpClient
}

2. 服务商1and1的限制可能性

1and1(现IONOS)确实有请求频率和连接数限制,短时间内发起大量GET请求可能被限流,导致部分请求被拦截返回空内容。

验证方式:在JSONParser中添加日志打印HTTP响应状态码:

HttpResponse httpResponse = httpClient.execute(httpGet);
Log.d("HTTP Status", "Code: " + httpResponse.getStatusLine().getStatusCode());

如果看到429(限流)或5xx错误,说明是服务商限制,解决方法是在分块请求之间添加短暂延迟:

// 每次请求后延迟100ms
Thread.sleep(100);

3. 更优的文本获取方案

分块JSON请求不是最优选择,推荐两种更高效的方式:

(1)直接返回纯文本而非JSON

修改PHP代码,直接输出截取的文本内容,避免JSON解析的开销和潜在错误:

if ((isset($_GET["bookid"]))&&(isset($_GET["start"]))&&(isset($_GET["length"]))) {
    $start = $_GET['start'];
    $length = $_GET['length'];
    $bookid = $_GET['bookid'];
    $result = mysql_query("SELECT SUBSTRING(file, $start, $length) AS file FROM book WHERE bookid = $bookid");
    if (!empty($result) && mysql_num_rows($result) > 0) {
        $result = mysql_fetch_array($result);
        header('Content-Type: text/plain; charset=utf-8');
        echo $result["file"];
    } else {
        header("HTTP/1.1 404 Not Found");
        echo "No content";
    }
}

Android端直接读取流拼接字符串,无需解析JSON。

(2)使用HTTP范围请求

如果服务器支持,实现HTTP Range Requests,让Android端通过一次请求的分块获取下载整个文本,这种方式更符合HTTP规范,适合大文件场景。


内容的提问来源于stack exchange,提问作者GC Apps Lab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:53:23