Android GZip to MySQL BLOB via PHP


Android GZip to MySQL BLOB via PHP

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

我的安卓应用程序中有一个很大的字符串。我使用

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

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

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上传代码进行了进一步的更改:

在安卓中,我已经改变了

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服务器。服务器中的结果数据不为空。我已经下载了数据并成功对其执行了枪击。如果我完全停止获得CRC不匹配,我会将其标记为正确答案。

在安卓中我改变了

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服务器。服务器中的结果数据不为空。我已经下载了数据并成功对其执行了枪击。

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

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