Prohibit fragments for empty streams

Signed-off-by: Bernd.Rederlechner@t-systems.com <bernd.rederlechner@t-systems.com>
This commit is contained in:
Bernd.Rederlechner@t-systems.com 2021-05-11 12:27:00 +00:00
parent 667d589688
commit 828dde4453
2 changed files with 50 additions and 19 deletions

View File

@ -97,29 +97,40 @@ trait S3ObjectTrait {
$count += $read; $count += $read;
}); });
$s3params = [
'bucket' => $this->bucket, if ($count === 0 && feof($countStream)) {
'key' => $urn, // This is an empty file so just touch it then
'part_size' => $this->uploadPartSize, $s3params = [
'params' => [ 'params' => $this->getSseKmsPutParameters(),
'ContentType' => $mimetype ];
] + $this->getSseKmsPutParameters(), $uploader = new ObjectUploader($this->getConnection(), $this->bucket, $urn, '', 'private', $s3params);
]; } else {
$uploader = new MultipartUploader($this->getConnection(), $countStream, $s3params); $s3params = [
'bucket' => $this->bucket,
'key' => $urn,
'part_size' => $this->uploadPartSize,
'params' => [
'ContentType' => $mimetype
] + $this->getSseKmsPutParameters(),
];
// maybe, we should also use ObjectUploader here in the future
// it does direct uploads for small files < 5MB and multipart otherwise
// $uploader = new ObjectUploader($this->getConnection(), $this->bucket, $urn, $countStream, 'private', $s3params);
$uploader = new MultipartUploader($this->getConnection(), $countStream, $s3params);
}
try { try {
$uploader->upload(); $uploader->upload();
} catch (S3MultipartUploadException $e) { } catch (S3MultipartUploadException $e) {
// This is an empty file so just touch it then // if anything goes wrong with multipart, make sure that you don´t poison s3 bucket with fragments
if ($count === 0 && feof($countStream)) { $this->getConnection()->abortMultipartUpload([
$s3params = [ 'Bucket' => $this->bucket,
'params' => $this->getSseKmsPutParameters(), 'Key' => $urn,
]; 'UploadId' => $uploader->getState()->getId()
$uploader = new ObjectUploader($this->getConnection(), $this->bucket, $urn, '', 'private', $s3params); ]);
$uploader->upload();
} else { throw $e;
throw $e;
}
} finally { } finally {
// this handles [S3] fclose(): supplied resource is not a valid stream resource #23373 // this handles [S3] fclose(): supplied resource is not a valid stream resource #23373
// see https://stackoverflow.com/questions/11247507/fclose-18-is-not-a-valid-stream-resource/11247555 // see https://stackoverflow.com/questions/11247507/fclose-18-is-not-a-valid-stream-resource/11247555

View File

@ -22,8 +22,10 @@
namespace Test\Files\ObjectStore; namespace Test\Files\ObjectStore;
use Icewind\Streams\Wrapper; use Icewind\Streams\Wrapper;
use Icewind\Streams\CallbackWrapper;
use OC\Files\ObjectStore\S3; use OC\Files\ObjectStore\S3;
class MultiPartUploadS3 extends S3 { class MultiPartUploadS3 extends S3 {
public function writeObject($urn, $stream, string $mimetype = null) { public function writeObject($urn, $stream, string $mimetype = null) {
$this->getConnection()->upload($this->bucket, $urn, $stream, 'private', [ $this->getConnection()->upload($this->bucket, $urn, $stream, 'private', [
@ -94,4 +96,22 @@ class S3Test extends ObjectStoreTest {
fseek($read, 100, SEEK_CUR); fseek($read, 100, SEEK_CUR);
$this->assertEquals(substr($data, 210, 100), fread($read, 100)); $this->assertEquals(substr($data, 210, 100), fread($read, 100));
} }
public function testEmptyUpload() {
$s3 = $this->getInstance();
$emptyStream = fopen('php://memory', 'w+');
$count = 0;
$countStream = CallbackWrapper::wrap($emptyStream, function ($read) use (&$count) {
$count += $read;
});
$this->assertEquals($count, 0);
$this->assertTrue(feof($countStream));
$s3->writeObject('emptyuploadtest', $emptyString);
$result = $s3->readObject('emptyuploadtest');
$this->assertEquals('', stream_get_contents($result));
}
} }