0

很少,在将 GZIP 压缩字符串发送到 MySQL 服务器并返回后,我会遇到 CRC Mismatch。以下是数据的流动方式。

我的 Android 应用程序中有一个大字符串。我使用将它压缩成一个字节数组

public synchronized static byte[] compress(String str) throws IOException {
    ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
    GZIPOutputStream gz_out = new GZIPOutputStream(os);
    gz_out.write(str.getBytes());
    gz_out.finish();
    gz_out.flush();
    gz_out.close();
    os.flush();
    os.close();

    byte[] test = os.toByteArray();
    String check = decompress(test, false); // making sure CRC Mismatch doesn't occur here.
    if (check == null) {
        return compress(str);
    }
    return test;
}

public synchronized static String decompress(byte[] compressed, boolean throwException) {
    try {
        final int BUFFER_SIZE = 32;
        ByteArrayInputStream is = new ByteArrayInputStream(compressed);
        GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
        StringBuilder string = new StringBuilder();
        byte[] data = new byte[BUFFER_SIZE];
        int bytesRead;
        while ((bytesRead = gis.read(data)) != -1) {
            string.append(new String(data, 0, bytesRead));
        }
        gis.close();
        is.close();
        return string.toString();
    } catch (IOException e) {
        if (throwException)
            throw new RuntimeException("Failed to decompress GZIP.", e);
        else {
            Log.e(log_tag, "Failed to decompress GZIP.", e);
            return null;
        }
    }
}

我像这样将它发送到我的服务器:

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    builder.addBinaryBody("data", data);
    InputStream is = null;
    try {
        HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, 20000);
        HttpConnectionParams.setSoTimeout(httpParams, 20000);
        HttpClient httpclient = new DefaultHttpClient(httpParams);
        HttpPost httppost = new HttpPost(SERVER_ADDRESS + "sendData.php");
        httppost.setEntity(builder.build());
        HttpResponse response = httpclient.execute(httppost);
        HttpEntity entity = response.getEntity();
        is = entity.getContent();
    } catch (Exception e) {
        Log.e("FriendlyFire", "Error in http connection", e);
        return null;
    }
    // Then read response...

sendData.php 看起来像这样:

//I read the bytes into a variable using 
$data = $_POST['data']; 

//I create a MySQL connection using 
$conn = new mysqli(...); 

//I prepare a statement using 
$stmt = $conn->prepare("CALL database.saveData(?);");

//I attach the data using 
$stmt->bind_param('s', $data); 

//I execute the statement using 
$stmt->execute(); 

//I close the statement using 
$stmt->close(); 

//I close the connection using 
$conn->close();

MySQL 过程如下所示:

database.saveData(data BLOB)
BEGIN
  INSERT INTO Table (columnName) VALUES (data);
END

要将数据下载到我的 Android 应用程序,我这样做:

HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 10000);
HttpConnectionParams.setSoTimeout(httpParams, 10000);
HttpClient httpclient = new DefaultHttpClient(httpParams);
HttpPost httppost = new HttpPost(SERVER_ADDRESS + "getData.php");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
byte[] data = EntityUtils.toByteArray(entity);

getData.php 看起来像这样:

$conn = mysqli_connect(...);
$db = mysqli_select_db($conn, "database");

$result = mysqli_query($conn, "CALL database.getData()");

$num_results = mysqli_num_rows($result); 
if ($num_results > 0){ 
  while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC))
  {
      $filedata = $row['columnName'];
      header("Content-length: ".strlen($filedata));
      header("Content-disposition: download; filename=data.bin");

      echo $filedata;
  }
} else {
  echo "EMPTY";
}

getData() 过程如下所示:

database.getData()
BEGIN
  SELECT columnName FROM Table LIMIT 1;
END

就像我说的那样,在尝试解压缩从服务器读取的内容后,我时不时会收到 CRC 不匹配错误。我怀疑问题出在我的 PHP 代码中,但也可能出在我的 Android 代码中。我有理由确定 SQL 过程很好。

GZIP 说 99.9% 的问题是由于传输是用 ASCII 而不是二进制完成的。我已尽力将数据保存为二进制,但我一定遗漏了一些东西。就像我说的,我怀疑我的 PHP 代码,尤其是上传代码。我直接从 MySQL 服务器下载了 BLOB,它也给出了 CRC 不匹配错误。

我已经尝试使用 bind_param('b', $data) 准备 mysqli 语句,但是 SQL 表中的结果数据是 0 字节,我在使用 'b' 参数类型时找不到太多内容。我应该使用 _FILES 而不是 _POST 吗?这甚至会有所作为吗?

编辑:我对 Android 和 PHP 上传代码做了进一步的修改:

在 Android 中我改变了

builder.addBinaryBody("data", data);

builder.addBinaryBody("data", data, Content.DEFAULT_BINARY, "data.bin");

和 PHP 的变化:

$filename = $_FILES['data']['tmp_name'];

//I create a MySQL connection using 
$conn = new mysqli(...); 

//I prepare a statement using 
$stmt = $conn->prepare("CALL database.saveData(?);");

//I attach the data using 
$stmt->bind_param('b', $data); 
$fp = fopen($filename, "r");
while ($data = fread($fp, 1024)) {
    $stmt2->send_long_data(0, $data);
}

//I execute the statement using 
$stmt->execute(); 

//I close the statement using 
$stmt->close(); 

//I close the connection using 
$conn->close();

所以这成功地使用'b'参数将数据发送到MySQL服务器。服务器中的结果数据不为空。我已经下载了数据并成功地对其执行了 gunzip。如果我完全停止得到 CRC 不匹配,我会将其标记为正确答案。

4

1 回答 1

0

在 Android 中我改变了

builder.addBinaryBody("data", data);

builder.addBinaryBody("data", data, Content.DEFAULT_BINARY, "data.bin");

前者似乎将数据放入 _POST 数组中,而后者将数据放入 _FILES 数组中。

和 PHP 的变化:

$filename = $_FILES['data']['tmp_name'];

//I create a MySQL connection using 
$conn = new mysqli(...); 

//I prepare a statement using 
$stmt = $conn->prepare("CALL database.saveData(?);");

//I attach the data using 
$stmt->bind_param('b', $data); 
$fp = fopen($filename, "r");
while ($data = fread($fp, 1024)) {
    $stmt2->send_long_data(0, $data);
}

//I execute the statement using 
$stmt->execute(); 

//I close the statement using 
$stmt->close(); 

//I close the connection using 
$conn->close();

所以这成功地使用'b'参数将数据发送到MySQL服务器。服务器中的结果数据不为空。我已经下载了数据并成功地对其执行了 gunzip。

从那以后我还没有遇到 CRC 不匹配错误。

希望我已经解释得足够好。

于 2013-12-05T10:41:31.727 回答