ごくまれに、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 の不一致が完全になくなったら、これを正解としてマークします。