0

ごくまれに、GZIP 圧縮された文字列を MySQL サーバーに送信してから戻した後に、CRC 不一致が発生します。データの流れはこんな感じ。

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 コード、特にアップロード コードを疑っています。BLOB を MySQL サーバーから直接ダウンロードしましたが、CRC 不一致エラーも発生します。

bind_param('b', $data) を使用して mysqli ステートメントを準備しようとしましたが、SQL テーブルの結果のデータは 0 バイトであり、'b' パラメーター タイプの使用についてはあまり見つかりません。_POST の代わりに _FILES を使用する必要がありますか? それは違いを生むでしょうか?

編集: 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