set mimetype for objects uploaded to object storages

Signed-off-by: Robin Appelman <robin@icewind.nl>
This commit is contained in:
Robin Appelman 2021-04-15 17:14:57 +02:00
parent d2ea068552
commit effb7dc8ba
No known key found for this signature in database
GPG Key ID: 42B69D8A64526EFB
11 changed files with 38 additions and 32 deletions

View File

@ -50,6 +50,7 @@ use OC\Files\Cache\CacheEntry;
use OC\Files\ObjectStore\S3ConnectionTrait; use OC\Files\ObjectStore\S3ConnectionTrait;
use OC\Files\ObjectStore\S3ObjectTrait; use OC\Files\ObjectStore\S3ObjectTrait;
use OCP\Constants; use OCP\Constants;
use OCP\Files\IMimeTypeDetector;
class AmazonS3 extends \OC\Files\Storage\Common { class AmazonS3 extends \OC\Files\Storage\Common {
use S3ConnectionTrait; use S3ConnectionTrait;
@ -68,12 +69,16 @@ class AmazonS3 extends \OC\Files\Storage\Common {
/** @var CappedMemoryCache|array */ /** @var CappedMemoryCache|array */
private $filesCache; private $filesCache;
/** @var IMimeTypeDetector */
private $mimeDetector;
public function __construct($parameters) { public function __construct($parameters) {
parent::__construct($parameters); parent::__construct($parameters);
$this->parseParams($parameters); $this->parseParams($parameters);
$this->objectCache = new CappedMemoryCache(); $this->objectCache = new CappedMemoryCache();
$this->directoryCache = new CappedMemoryCache(); $this->directoryCache = new CappedMemoryCache();
$this->filesCache = new CappedMemoryCache(); $this->filesCache = new CappedMemoryCache();
$this->mimeDetector = \OC::$server->get(IMimeTypeDetector::class);
} }
/** /**
@ -573,7 +578,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
try { try {
if (!$this->file_exists($path)) { if (!$this->file_exists($path)) {
$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); $mimeType = $this->mimeDetector->detectPath($path);
$this->getConnection()->putObject([ $this->getConnection()->putObject([
'Bucket' => $this->bucket, 'Bucket' => $this->bucket,
'Key' => $this->cleanKey($path), 'Key' => $this->cleanKey($path),
@ -684,7 +689,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
public function writeBack($tmpFile, $path) { public function writeBack($tmpFile, $path) {
try { try {
$source = fopen($tmpFile, 'r'); $source = fopen($tmpFile, 'r');
$this->writeObject($path, $source); $this->writeObject($path, $source, $this->mimeDetector->detectPath($path));
$this->invalidateCache($path); $this->invalidateCache($path);
unlink($tmpFile); unlink($tmpFile);

View File

@ -47,6 +47,7 @@ use GuzzleHttp\Psr7\Uri;
use Icewind\Streams\CallbackWrapper; use Icewind\Streams\CallbackWrapper;
use Icewind\Streams\IteratorDirectory; use Icewind\Streams\IteratorDirectory;
use OC\Files\ObjectStore\SwiftFactory; use OC\Files\ObjectStore\SwiftFactory;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\StorageBadConfigException; use OCP\Files\StorageBadConfigException;
use OCP\ILogger; use OCP\ILogger;
use OpenStack\Common\Error\BadResponseError; use OpenStack\Common\Error\BadResponseError;
@ -76,6 +77,9 @@ class Swift extends \OC\Files\Storage\Common {
/** @var \OC\Files\ObjectStore\Swift */ /** @var \OC\Files\ObjectStore\Swift */
private $objectStore; private $objectStore;
/** @var IMimeTypeDetector */
private $mimeDetector;
/** /**
* Key value cache mapping path to data object. Maps path to * Key value cache mapping path to data object. Maps path to
* \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
@ -205,6 +209,7 @@ class Swift extends \OC\Files\Storage\Common {
); );
$this->objectStore = new \OC\Files\ObjectStore\Swift($this->params, $this->connectionFactory); $this->objectStore = new \OC\Files\ObjectStore\Swift($this->params, $this->connectionFactory);
$this->bucket = $params['bucket']; $this->bucket = $params['bucket'];
$this->mimeDetector = \OC::$server->get(IMimeTypeDetector::class);
} }
public function mkdir($path) { public function mkdir($path) {
@ -466,7 +471,7 @@ class Swift extends \OC\Files\Storage\Common {
} }
return true; return true;
} else { } else {
$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); $mimeType = $this->mimeDetector->detectPath($path);
$this->getContainer()->createObject([ $this->getContainer()->createObject([
'name' => $path, 'name' => $path,
'content' => '', 'content' => '',
@ -588,7 +593,7 @@ class Swift extends \OC\Files\Storage\Common {
public function writeBack($tmpFile, $path) { public function writeBack($tmpFile, $path) {
$fileData = fopen($tmpFile, 'r'); $fileData = fopen($tmpFile, 'r');
$this->objectStore->writeObject($path, $fileData); $this->objectStore->writeObject($path, $fileData, $this->mimeDetector->detectPath($path));
// invalidate target object to force repopulation on fetch // invalidate target object to force repopulation on fetch
$this->objectCache->remove($path); $this->objectCache->remove($path);
unlink($tmpFile); unlink($tmpFile);

View File

@ -24,6 +24,7 @@
namespace OC\Files\ObjectStore; namespace OC\Files\ObjectStore;
use MicrosoftAzure\Storage\Blob\BlobRestProxy; use MicrosoftAzure\Storage\Blob\BlobRestProxy;
use MicrosoftAzure\Storage\Blob\Models\CreateBlockBlobOptions;
use MicrosoftAzure\Storage\Common\Exceptions\ServiceException; use MicrosoftAzure\Storage\Common\Exceptions\ServiceException;
use OCP\Files\ObjectStore\IObjectStore; use OCP\Files\ObjectStore\IObjectStore;
@ -100,13 +101,12 @@ class Azure implements IObjectStore {
return $blob->getContentStream(); return $blob->getContentStream();
} }
/** public function writeObject($urn, $stream, string $mimetype = null) {
* @param string $urn the unified resource name used to identify the object $options = new CreateBlockBlobOptions();
* @param resource $stream stream with the data to write if ($mimetype) {
* @throws \Exception when something goes wrong, message will be logged $options->setContentType($mimetype);
*/ }
public function writeObject($urn, $stream) { $this->getBlobClient()->createBlockBlob($this->containerName, $urn, $stream, $options);
$this->getBlobClient()->createBlockBlob($this->containerName, $urn, $stream);
} }
/** /**

View File

@ -486,13 +486,13 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
]); ]);
$size = $writtenSize; $size = $writtenSize;
}); });
$this->objectStore->writeObject($urn, $countStream); $this->objectStore->writeObject($urn, $countStream, $mimetype);
if (is_resource($countStream)) { if (is_resource($countStream)) {
fclose($countStream); fclose($countStream);
} }
$stat['size'] = $size; $stat['size'] = $size;
} else { } else {
$this->objectStore->writeObject($urn, $stream); $this->objectStore->writeObject($urn, $stream, $mimetype);
} }
} catch (\Exception $ex) { } catch (\Exception $ex) {
if (!$exists) { if (!$exists) {

View File

@ -78,10 +78,11 @@ trait S3ObjectTrait {
/** /**
* @param string $urn the unified resource name used to identify the object * @param string $urn the unified resource name used to identify the object
* @param resource $stream stream with the data to write * @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 * @throws \Exception when something goes wrong, message will be logged
* @since 7.0.0 * @since 7.0.0
*/ */
public function writeObject($urn, $stream) { public function writeObject($urn, $stream, string $mimetype = null) {
$count = 0; $count = 0;
$countStream = CallbackWrapper::wrap($stream, function ($read) use (&$count) { $countStream = CallbackWrapper::wrap($stream, function ($read) use (&$count) {
$count += $read; $count += $read;
@ -91,6 +92,9 @@ trait S3ObjectTrait {
'bucket' => $this->bucket, 'bucket' => $this->bucket,
'key' => $urn, 'key' => $urn,
'part_size' => $this->uploadPartSize, 'part_size' => $this->uploadPartSize,
'params' => [
'ContentType' => $mimetype
]
]); ]);
try { try {

View File

@ -65,13 +65,7 @@ class StorageObjectStore implements IObjectStore {
throw new \Exception(); throw new \Exception();
} }
/** public function writeObject($urn, $stream, string $mimetype = null) {
* @param string $urn the unified resource name used to identify the object
* @param resource $stream stream with the data to write
* @throws \Exception when something goes wrong, message will be logged
* @since 7.0.0
*/
public function writeObject($urn, $stream) {
$handle = $this->storage->fopen($urn, 'w'); $handle = $this->storage->fopen($urn, 'w');
if ($handle) { if ($handle) {
stream_copy_to_stream($stream, $handle); stream_copy_to_stream($stream, $handle);

View File

@ -74,12 +74,7 @@ class Swift implements IObjectStore {
return $this->params['container']; return $this->params['container'];
} }
/** public function writeObject($urn, $stream, string $mimetype = null) {
* @param string $urn the unified resource name used to identify the object
* @param resource $stream stream with the data to write
* @throws \Exception from openstack lib when something goes wrong
*/
public function writeObject($urn, $stream) {
$tmpFile = \OC::$server->getTempManager()->getTemporaryFile('swiftwrite'); $tmpFile = \OC::$server->getTempManager()->getTemporaryFile('swiftwrite');
file_put_contents($tmpFile, $stream); file_put_contents($tmpFile, $stream);
$handle = fopen($tmpFile, 'rb'); $handle = fopen($tmpFile, 'rb');
@ -88,12 +83,14 @@ class Swift implements IObjectStore {
$this->getContainer()->createObject([ $this->getContainer()->createObject([
'name' => $urn, 'name' => $urn,
'stream' => stream_for($handle), 'stream' => stream_for($handle),
'contentType' => $mimetype,
]); ]);
} else { } else {
$this->getContainer()->createLargeObject([ $this->getContainer()->createLargeObject([
'name' => $urn, 'name' => $urn,
'stream' => stream_for($handle), 'stream' => stream_for($handle),
'segmentSize' => SWIFT_SEGMENT_SIZE, 'segmentSize' => SWIFT_SEGMENT_SIZE,
'contentType' => $mimetype,
]); ]);
} }
} }

View File

@ -52,10 +52,11 @@ interface IObjectStore {
/** /**
* @param string $urn the unified resource name used to identify the object * @param string $urn the unified resource name used to identify the object
* @param resource $stream stream with the data to write * @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 * @throws \Exception when something goes wrong, message will be logged
* @since 7.0.0 * @since 7.0.0
*/ */
public function writeObject($urn, $stream); public function writeObject($urn, $stream, string $mimetype = null);
/** /**
* @param string $urn the unified resource name used to identify the object * @param string $urn the unified resource name used to identify the object

View File

@ -40,8 +40,8 @@ class FailDeleteObjectStore implements IObjectStore {
return $this->objectStore->readObject($urn); return $this->objectStore->readObject($urn);
} }
public function writeObject($urn, $stream) { public function writeObject($urn, $stream, string $mimetype = null) {
return $this->objectStore->writeObject($urn, $stream); return $this->objectStore->writeObject($urn, $stream, $mimetype);
} }
public function deleteObject($urn) { public function deleteObject($urn) {

View File

@ -40,7 +40,7 @@ class FailWriteObjectStore implements IObjectStore {
return $this->objectStore->readObject($urn); return $this->objectStore->readObject($urn);
} }
public function writeObject($urn, $stream) { public function writeObject($urn, $stream, string $mimetype = null) {
// emulate a failed write that didn't throw an error // emulate a failed write that didn't throw an error
return true; return true;
} }

View File

@ -25,7 +25,7 @@ use Icewind\Streams\Wrapper;
use OC\Files\ObjectStore\S3; use OC\Files\ObjectStore\S3;
class MultiPartUploadS3 extends S3 { class MultiPartUploadS3 extends S3 {
public function writeObject($urn, $stream) { public function writeObject($urn, $stream, string $mimetype = null) {
$this->getConnection()->upload($this->bucket, $urn, $stream, 'private', [ $this->getConnection()->upload($this->bucket, $urn, $stream, 'private', [
'mup_threshold' => 1, 'mup_threshold' => 1,
]); ]);