Merge pull request #51779 from nextcloud/object-store-filename

pull/52543/head
Kate 5 months ago committed by GitHub
commit 9592390070
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      lib/private/Files/ObjectStore/ObjectStoreStorage.php
  2. 12
      lib/private/Files/ObjectStore/S3.php
  3. 45
      lib/private/Files/ObjectStore/S3ObjectTrait.php
  4. 11
      lib/public/Files/ObjectStore/IObjectStoreMetaData.php

@ -22,6 +22,7 @@ use OCP\Files\FileInfo;
use OCP\Files\GenericFileException;
use OCP\Files\NotFoundException;
use OCP\Files\ObjectStore\IObjectStore;
use OCP\Files\ObjectStore\IObjectStoreMetaData;
use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload;
use OCP\Files\Storage\IChunkedFileWrite;
use OCP\Files\Storage\IStorage;
@ -479,6 +480,11 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
$mimetypeDetector = \OC::$server->getMimeTypeDetector();
$mimetype = $mimetypeDetector->detectPath($path);
$metadata = [
'mimetype' => $mimetype,
'original-storage' => $this->getId(),
'original-path' => $path,
];
$stat['mimetype'] = $mimetype;
$stat['etag'] = $this->getETag($path);
@ -507,13 +513,21 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
]);
$size = $writtenSize;
});
$this->objectStore->writeObject($urn, $countStream, $mimetype);
if ($this->objectStore instanceof IObjectStoreMetaData) {
$this->objectStore->writeObjectWithMetaData($urn, $countStream, $metadata);
} else {
$this->objectStore->writeObject($urn, $countStream, $metadata['mimetype']);
}
if (is_resource($countStream)) {
fclose($countStream);
}
$stat['size'] = $size;
} else {
$this->objectStore->writeObject($urn, $stream, $mimetype);
if ($this->objectStore instanceof IObjectStoreMetaData) {
$this->objectStore->writeObjectWithMetaData($urn, $stream, $metadata);
} else {
$this->objectStore->writeObject($urn, $stream, $metadata['mimetype']);
}
if (is_resource($stream)) {
fclose($stream);
}

@ -95,6 +95,16 @@ class S3 implements IObjectStore, IObjectStoreMultiPartUpload, IObjectStoreMetaD
]);
}
private function parseS3Metadata(array $metadata): array {
$result = [];
foreach ($metadata as $key => $value) {
if (str_starts_with($key, 'x-amz-meta-')) {
$result[substr($key, strlen('x-amz-meta-'))] = $value;
}
}
return $result;
}
public function getObjectMetaData(string $urn): array {
$object = $this->getConnection()->headObject([
'Bucket' => $this->bucket,
@ -104,7 +114,7 @@ class S3 implements IObjectStore, IObjectStoreMultiPartUpload, IObjectStoreMetaD
'mtime' => $object['LastModified'],
'etag' => trim($object['ETag'], '"'),
'size' => (int)($object['Size'] ?? $object['ContentLength']),
];
] + $this->parseS3Metadata($object['Metadata'] ?? []);
}
public function listObjects(string $prefix = ''): \Iterator {

@ -77,22 +77,32 @@ trait S3ObjectTrait {
return $fh;
}
private function buildS3Metadata(array $metadata): array {
$result = [];
foreach ($metadata as $key => $value) {
$result['x-amz-meta-' . $key] = $value;
}
return $result;
}
/**
* Single object put helper
*
* @param string $urn the unified resource name used to identify the object
* @param StreamInterface $stream stream with the data to write
* @param string|null $mimetype the mimetype to set for the remove object @since 22.0.0
* @param array $metaData the metadata to set for the object
* @throws \Exception when something goes wrong, message will be logged
*/
protected function writeSingle(string $urn, StreamInterface $stream, ?string $mimetype = null): void {
protected function writeSingle(string $urn, StreamInterface $stream, array $metaData): void {
$mimetype = $metaData['mimetype'] ?? null;
unset($metaData['mimetype']);
$this->getConnection()->putObject([
'Bucket' => $this->bucket,
'Key' => $urn,
'Body' => $stream,
'ACL' => 'private',
'ContentType' => $mimetype,
'Metadata' => $this->buildS3Metadata($metaData),
'StorageClass' => $this->storageClass,
] + $this->getSSECParameters());
}
@ -103,10 +113,12 @@ trait S3ObjectTrait {
*
* @param string $urn the unified resource name used to identify the object
* @param StreamInterface $stream stream with the data to write
* @param string|null $mimetype the mimetype to set for the remove object
* @param array $metaData the metadata to set for the object
* @throws \Exception when something goes wrong, message will be logged
*/
protected function writeMultiPart(string $urn, StreamInterface $stream, ?string $mimetype = null): void {
protected function writeMultiPart(string $urn, StreamInterface $stream, array $metaData): void {
$mimetype = $metaData['mimetype'] ?? null;
unset($metaData['mimetype']);
$uploader = new MultipartUploader($this->getConnection(), $stream, [
'bucket' => $this->bucket,
'concurrency' => $this->concurrency,
@ -114,6 +126,7 @@ trait S3ObjectTrait {
'part_size' => $this->uploadPartSize,
'params' => [
'ContentType' => $mimetype,
'Metadata' => $this->buildS3Metadata($metaData),
'StorageClass' => $this->storageClass,
] + $this->getSSECParameters(),
]);
@ -131,15 +144,15 @@ trait S3ObjectTrait {
}
}
/**
* @param string $urn the unified resource name used to identify the object
* @param resource $stream stream with the data to write
* @param string|null $mimetype the mimetype to set for the remove object @since 22.0.0
* @throws \Exception when something goes wrong, message will be logged
* @since 7.0.0
*/
public function writeObject($urn, $stream, ?string $mimetype = null) {
$metaData = [];
if ($mimetype) {
$metaData['mimetype'] = $mimetype;
}
$this->writeObjectWithMetaData($urn, $stream, $metaData);
}
public function writeObjectWithMetaData(string $urn, $stream, array $metaData): void {
$canSeek = fseek($stream, 0, SEEK_CUR) === 0;
$psrStream = Utils::streamFor($stream);
@ -154,16 +167,16 @@ trait S3ObjectTrait {
$buffer->seek(0);
if ($buffer->getSize() < $this->putSizeLimit) {
// buffer is fully seekable, so use it directly for the small upload
$this->writeSingle($urn, $buffer, $mimetype);
$this->writeSingle($urn, $buffer, $metaData);
} else {
$loadStream = new Psr7\AppendStream([$buffer, $psrStream]);
$this->writeMultiPart($urn, $loadStream, $mimetype);
$this->writeMultiPart($urn, $loadStream, $metaData);
}
} else {
if ($size < $this->putSizeLimit) {
$this->writeSingle($urn, $psrStream, $mimetype);
$this->writeSingle($urn, $psrStream, $metaData);
} else {
$this->writeMultiPart($urn, $psrStream, $mimetype);
$this->writeMultiPart($urn, $psrStream, $metaData);
}
}
$psrStream->close();

@ -9,7 +9,7 @@ namespace OCP\Files\ObjectStore;
/**
* Interface IObjectStoreMetaData
*
* @psalm-type ObjectMetaData = array{mtime?: \DateTime, etag?: string, size?: int, mimetype?: string, filename?: string}
* @psalm-type ObjectMetaData = array{mtime?: \DateTime, etag?: string, size?: int, mimetype?: string, filename?: string, original-path?: string, original-storage?: string}
*
* @since 32.0.0
*/
@ -35,4 +35,13 @@ interface IObjectStoreMetaData {
* @since 32.0.0
*/
public function listObjects(string $prefix = ''): \Iterator;
/**
* @param string $urn the unified resource name used to identify the object
* @param resource $stream stream with the data to write
* @param ObjectMetaData $metaData the metadata to set for the object
* @throws \Exception when something goes wrong, message will be logged
* @since 32.0.0
*/
public function writeObjectWithMetaData(string $urn, $stream, array $metaData): void;
}

Loading…
Cancel
Save