2016-12-13 1 views
2

Ich möchte große Dateien aus Google Cloud Storage mit der von Google bereitgestellten Java-Bibliothek com.google.cloud.storage herunterladen. Ich habe Arbeitscode, aber ich habe noch eine Frage und ein Hauptproblem:So laden Sie eine große Datei aus Google Cloud Storage mithilfe von Java mit Prüfsummensteuerung

Mein Hauptanliegen ist, wann ist der Dateiinhalt tatsächlich heruntergeladen? Während (Verweise auf den folgenden Code) storage.get(blobId), während blob.reader() oder während reader.read(bytes)? Dies wird sehr wichtig, wenn es darum geht, wie mit einer ungültigen Prüfsumme zu behandeln, was muss ich tun, um tatsächlich auszulösen, dass die Datei wieder über das Netzwerk abgerufen wird?

Die einfachere Frage ist: Gibt es eingebaute Funktionalität, um MD5 (oder CRC32C) auf die erhaltene Datei in der Google Bibliothek zu tun? Vielleicht muss ich es nicht selbst implementieren.

Hier versucht, meine Methode große Dateien von Google Cloud Storage herunterladen:

private static final int MAX_NUMBER_OF_TRIES = 3; 
public Path downloadFile(String storageFileName, String bucketName) throws IOException { 
    // In my real code, this is a field populated in the constructor. 
    Storage storage = Objects.requireNonNull(StorageOptions.getDefaultInstance().getService()); 

    BlobId blobId = BlobId.of(bucketName, storageFileName); 
    Path outputFile = Paths.get(storageFileName.replaceAll("/", "-")); 
    int retryCounter = 1; 
    Blob blob; 
    boolean checksumOk; 
    MessageDigest messageDigest; 
    try { 
     messageDigest = MessageDigest.getInstance("MD5"); 
    } catch (NoSuchAlgorithmException ex) { 
     throw new RuntimeException(ex); 
    } 

    do { 
     LOGGER.debug("Start download file {} from bucket {} to Content Store (try {})", storageFileName, bucketName, retryCounter); 
     blob = storage.get(blobId); 
     if (null == blob) { 
      throw new CloudStorageCommunicationException("Failed to download file after " + retryCounter + " tries."); 
     } 
     if (Files.exists(outputFile)) { 
      Files.delete(outputFile); 
     } 
     try (ReadChannel reader = blob.reader(); 
      FileChannel channel = new FileOutputStream(outputFile.toFile(), true).getChannel()) { 
      ByteBuffer bytes = ByteBuffer.allocate(128 * 1024); 
      int bytesRead = reader.read(bytes); 
      while (bytesRead > 0) { 
       bytes.flip(); 
       messageDigest.update(bytes.array(), 0, bytesRead); 
       channel.write(bytes); 
       bytes.clear(); 
       bytesRead = reader.read(bytes); 
      } 
     } 
     String checksum = Base64.encodeBase64String(messageDigest.digest()); 
     checksumOk = checksum.equals(blob.getMd5()); 
     if (!checksumOk) { 
      Files.delete(outputFile); 
      messageDigest.reset(); 
     } 
    } while (++retryCounter <= MAX_NUMBER_OF_TRIES && !checksumOk); 
    if (!checksumOk) { 
     throw new CloudStorageCommunicationException("Failed to download file after " + MAX_NUMBER_OF_TRIES + " tries."); 
    } 
    return outputFile; 
} 

Antwort

2

Die Google-Cloud-Java-Speicherbibliothek validiert keine Prüfsummen, wenn Daten über die normale HTTPS/TCP-Korrektheitsprüfung hinaus gelesen werden. Wenn das MD5 der empfangenen Daten mit dem bekannten MD5 verglichen würde, müsste es die gesamte Datei herunterladen, bevor es Ergebnisse von read() zurücksenden könnte, was für sehr große Dateien undurchführbar wäre.

Was Sie tun, ist eine gute Idee, wenn Sie den zusätzlichen Schutz des Vergleichs von MD5s benötigen. Wenn dies eine einmalige Aufgabe ist, können Sie das Befehlszeilentool gsutil verwenden, das diese Art von zusätzlicher Prüfung durchführt.

+0

Macht Sinn! Wenn ich eine ungültige Prüfsumme feststelle, gibt es eine Möglichkeit, die Datei com.google.cloud.storage dazu zu zwingen, die Datei erneut herunterzuladen und nicht aus einem Cache zu holen? –

+0

würde ich nicht stören. Wenn Sie eine ungültige Prüfsumme erhalten, war der Fehler sehr wahrscheinlich ein Netzwerkproblem und wird beim zweiten Mal nicht auftreten. Außerdem werden HTTPS-Lesevorgänge, die mit Anmeldeinformationen vorgenommen werden, mit ziemlicher Sicherheit nicht zwischengespeichert, es sei denn, Sie befinden sich hinter einer besonders seltsamen Unternehmensfirewall. Nichtsdestotrotz könnten Sie die meisten Caches mit Sicherheit überspringen, indem Sie einfach einen zusätzlichen Nonsense-URL-Parameter hinzufügen, wie zum Beispiel "& skipCaching = 12345". –

+0

Das Problem ist nicht der HTTP-Cache, sondern der Cache in der von google bereitgestellten Java-Bibliothek (Link in der obigen Frage). JavaDoc for ReadChannel gibt an, dass "Implementierungen dieser Klasse Daten möglicherweise intern puffern, um Remoteaufrufe zu reduzieren". Hier ist eine Seite, die über die Überprüfung der Prüfsumme spricht, aber meine Frage nicht beantwortet, soweit ich das sehe: https://cloud.google.com/storage/docs/hashes-etags Ich arbeite also leider nicht direkt mit URLs. –

0

Da die JavaDoc von ReadChannel sagt:

Implementationen dieser Klasse intern Datenpuffer können Remote-Aufrufe zu reduzieren .

So ist die Implementierung Sie von blob.reader() bekommen konnte die gesamte Datei, einige Bytes oder nichts zwischenzuspeichern und nur Byte für Byte holen, wenn Sie read() nennen. Du wirst es nie wissen und es sollte dir egal sein.

Wie nur read() wirft eine IOException und die anderen Methoden, die Sie nicht tun, würde ich sagen, dass nur Aufruf read() tatsächlich Zeug herunterladen wird. Sie können dies auch in the sources der lib sehen.

Btw. Trotz des Beispiels in den JavaDocs der Bibliothek sollten Sie nach >= 0 suchen, nicht nach > 0. 0 bedeutet nur, dass nichts gelesen wurde, nicht dass das Ende des Streams erreicht wurde. Das Ende des Streams wird durch die Rückgabe von -1 signalisiert.

Zum erneuten Versuch nach einer fehlgeschlagenen Prüfsummenprüfung, holen Sie sich einen neuen Reader aus dem Blob. Wenn etwas die heruntergeladenen Daten zwischenspeichert, dann der Leser selbst. Wenn Sie also einen neuen Reader aus dem Blob erhalten, wird die Datei von der Ferne heruntergeladen.

+0

Ja, ich habe das JavaDoc gelesen. Wie Sie erwähnen, hilft es nicht viel. Da dies keine Antwort ist, nur eine Aussage, dass du so ahnungslos wie ich bin, werde ich das abstimmen. Auch der Byte-Check sollte in Ordnung sein. Die Überprüfung erfolgt nach dem JavaDoc und dem Blob :: Reader, also gehe ich davon aus, dass der Kanal im Sperrmodus ist. –

+0

Ich bin nicht so ahnungslos wie du. Wie gesagt, die Lese-Methode wird das Netzwerk lesen. Sie können dies anhand der ausgelösten IOException sehen.Wenn man eine absolut gültige Antwort auf eine Antwort gibt, werden die Leute nicht ermutigt, Ihnen überhaupt weitere Antworten zu geben. Ein fehlerhaftes Beispiel zu finden bedeutet nicht, dass Sie den Fehler in Ihrem eigenen Code wiederholen müssen. Return-Code von '0' bedeutet einfach nicht, dass das Ende des Stream erreicht ist, das ist eine Tatsache, die kein schlecht geschriebenes Beispiel ändern kann. – Vampire

+0

Ich lese nur JavaDoc zu ReadableByteChannel, "Es ist jedoch garantiert, dass, wenn ein Kanal im Blockiermodus ist und mindestens ein Byte im Puffer übrig bleibt, diese Methode blockiert, bis mindestens ein Byte gelesen wird." –

Verwandte Themen