An error occurred while loading the file. Please try again.
-
ada66055
<?php declare(strict_types=1);
/*
* Copyright (C) 2015-2017 IRSTEA
* All rights reserved.
*/
namespace Irstea\FileUploadBundle\Entity;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Gaufrette\Exception\FileNotFound;
use Gaufrette\Filesystem;
use Gaufrette\StreamMode;
use InvalidArgumentException;
use Irstea\FileUploadBundle\Model\UploadedFileInterface;
use Irstea\FileUploadBundle\Utils\MimeTypeIcon;
use Rhumsaa\Uuid\Uuid;
/**
* @ORM\Entity(repositoryClass="Irstea\FileUploadBundle\Entity\Repository\UploadedFileRepository")
* @ORM\EntityListeners({
* "Irstea\FileUploadBundle\Listener\UploadedFileListener",
* "Irstea\FileUploadBundle\Listener\CreationDataListener"
* })
* @ORM\HasLifecycleCallbacks
*/
class UploadedFile implements UploadedFileInterface
{
/**
* Taille de bloc utilisé pour les copies.
*
* @var int
*/
public static $copyBlockSize = 8192;
public const ORPHAN_PREFIX = 'orphan/';
/**
* @ORM\Id
* @ORM\Column(type="guid")
*
* @var string
*/
private $id;
/**
* @ORM\Column(type="string", length=1024)
*
* @var string
*/
private $displayName;
/**
* @ORM\Column(type="string", length=1024)
*
* @var string
*/
private $path;
/**
* @var string
*/
private $actualPath;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*
* @var string
*/
private $mimeType;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
/**
* @ORM\Column(type="integer", nullable=true)
*
* @var int
*/
private $size;
/**
* @ORM\Column(type="string", length=64, nullable=true)
*
* @var string
*/
private $checksum;
/**
* @ORM\Column(type="string", length=10)
*
* @var string
*/
private $etat = self::ETAT_EN_COURS;
/**
* @ORM\Column(type="datetime")
*
* @var DateTime
*/
private $createdAt;
/**
* @ORM\Column(type="string", nullable=true)
*
* @var string
*/
private $createdBy;
/**
* @ORM\Column(type="string", nullable=true)
*
* @var string
*/
private $createdFrom;
/**
* @ORM\Column(type="json_array", nullable=true)
*
* @var array
*/
private $metadata;
/**
* @ORM\Column(type="string", length=256, nullable=true)
*
* @var string
*/
private $description;
/**
* @var Filesystem
*/
private $filesystem;
/** Contient le nom de chemin local.
* @var string
*/
private $localTempPath;
/** Crée un UploadedFile.
* @internal
*/
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
public function __construct()
{
$this->id = Uuid::uuid4()->toString();
$this->actualPath = $this->path = self::ORPHAN_PREFIX . $this->id;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function setDisplayName($displayName)
{
$this->displayName = $displayName;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDisplayName()
{
return $this->displayName;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return $this->path;
}
/**
* @return string
*/
public function getActualPath()
{
if (null === $this->actualPath) {
$this->actualPath = $this->path;
}
return $this->actualPath;
}
/**
* {@inheritdoc}
*/
public function setPath($path)
{
if (!static::isSafePath($path)) {
throw new InvalidArgumentException("Unsafe path: $path");
}
$this->path = trim($path, '/');
return $this;
}
/**
* {@inheritdoc}
*/
public function moveTo($newDir)
{
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
$this->setPath(rtrim($newDir, '/') . '/' . pathinfo($this->path, PATHINFO_FILENAME));
}
/**
* {@inheritdoc}
*/
public function setMimeType($mimeType)
{
$this->mimeType = $mimeType;
return $this;
}
/**
* {@inheritdoc}
*/
public function getMimeType()
{
return $this->mimeType;
}
/**
* {@inheritdoc}
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
/**
* {@inheritdoc}
*/
public function getSize()
{
return $this->size;
}
/**
* {@inheritdoc}
*/
public function setChecksum($checksum)
{
$this->checksum = $checksum;
return $this;
}
/**
* {@inheritdoc}
*/
public function getChecksum()
{
return $this->checksum;
}
/**
* {@inheritdoc}
*/
public function setEtat($etat)
{
if (!in_array(
$etat,
[
self::ETAT_CORROMPU,
self::ETAT_EN_COURS,
self::ETAT_MANQUANT,
self::ETAT_NORMAL,
self::ETAT_ORPHELIN,
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
self::ETAT_REJETE,
],
true
)) {
throw new InvalidArgumentException(sprintf("Etat invalide: '%s'", (string) $etat));
}
// Déplace le fichier hors de l'orphelinat quand on passe d'orphelin à nouveau
if ($this->etat === self::ETAT_ORPHELIN && $etat === self::ETAT_NORMAL && 0 === strpos($this->path, self::ORPHAN_PREFIX)) {
$this->path = substr($this->path, strlen(self::ORPHAN_PREFIX));
}
$this->etat = $etat;
return $this;
}
/**
* {@inheritdoc}
*/
public function getEtat()
{
return $this->etat;
}
/**
* {@inheritdoc}
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* {@inheritdoc}
*/
public function setMetadata(array $metadata = null)
{
$this->metadata = $metadata;
return $this;
}
/**
* {@inheritdoc}
*/
public function getMetadata()
{
return $this->metadata;
}
/**
* {@inheritdoc}
*/
public function __toString()
{
$unit = '';
$size = $this->size ?: 0;
if ($size >= 10240) {
$size /= 1024;
$unit = 'k';
if ($size >= 10240) {
$size /= 1024;
$unit = 'm';
}
}
return sprintf('%s (%s, %d%so)', $this->displayName, $this->mimeType ?: '?/?', $size, $unit);
}
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
/**
* @param Filesystem $filesystem
*
* @return self
*
* @internal
*/
public function setFilesystem(Filesystem $filesystem)
{
$this->filesystem = $filesystem;
return $this;
}
/**
* {@inheritdoc}
*/
public function validate()
{
if (self::ETAT_EN_COURS === $this->getEtat()) {
return;
}
$filesystem = $this->filesystem;
$path = $this->getActualPath();
if (!$filesystem->has($path)) {
$this->setEtat(self::ETAT_MANQUANT);
return;
}
if ($filesystem->size($path) !== $this->size || $filesystem->checksum($path) !== $this->checksum) {
$this->setEtat(self::ETAT_CORROMPU);
return;
}
}
/**
* {@inheritdoc}
*/
public function isValid()
{
return $this->getEtat() === self::ETAT_ORPHELIN || $this->getEtat() === self::ETAT_NORMAL;
}
/**
* {@inheritdoc}
*/
public function isOrphelin()
{
return $this->getEtat() === self::ETAT_ORPHELIN;
}
/**
* {@inheritdoc}
*/
public function getLastModified()
{
try {
return new \DateTime(sprintf('@%d', $this->filesystem->mtime($this->getActualPath())));
} catch (FileNotFound $ex) {
return null;
}
}
/**
* {@inheritdoc}
*/
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
public function getContent()
{
return $this->filesystem->read($this->getActualPath());
}
/**
* {@inheritdoc}
*/
public function setContent($content)
{
return $this->filesystem->write($this->getActualPath(), $content, true);
}
/**
* {@inheritdoc}
*/
public function copyFrom($source, $maxlen = -1, $writeOffset = 0)
{
if ($maxlen === 0) {
return 0;
}
$stream = $this->filesystem->createStream($this->getActualPath());
$stream->open(new StreamMode('cb'));
$stream->seek($writeOffset);
if (false !== $fileHandle = $stream->cast(STREAM_CAST_AS_STREAM)) {
// Utilise stream_copy_to_stream si le Gaufrette\Stream peut nous retourner un filehandle
$copied = stream_copy_to_stream($source, $fileHandle, $maxlen);
} else {
// Sinon fait une copie par blocs (moins performant)
if ($maxlen === -1) {
$maxlen = PHP_INT_MAX;
}
$copied = 0;
while (!feof($source) && $copied <= $maxlen) {
$copied += $stream->write(fread($source, min(static::$copyBlockSize, $maxlen - $copied)));
}
}
$stream->close();
return $copied;
}
/**
* {@inheritdoc}
*/
public function copyTo($dest, $maxlen = -1, $readOffset = 0)
{
if ($maxlen === -1) {
$actualLength = $this->getSize() - $readOffset;
} else {
$actualLength = min($maxlen, $this->getSize() - $readOffset);
}
if ($actualLength <= 0) {
return 0;
}
$stream = $this->filesystem->createStream($this->getActualPath());
$stream->open(new StreamMode('rb'));
$stream->seek($readOffset);
if (false !== $fileHandle = $stream->cast(STREAM_CAST_AS_STREAM)) {
// Utilise stream_copy_to_stream si le Stream nous renvoie un filehandle
$copied = stream_copy_to_stream($fileHandle, $dest, $actualLength);
} else {
// Sinon, on fait ça à la main par blocs de 8ko
$copied = 0;
while (!$stream->eof() && $copied < $actualLength) {
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
$copied += fwrite($dest, $stream->read(min(static::$copyBlockSize, $actualLength - $copied)));
}
}
$stream->close();
return $copied;
}
/** Vérifie si un chemin est "safe".
* @param string $path
*
* @return bool
*
* @internal
*/
public static function isSafePath($path)
{
/**
* @return string
*/
$parts = explode('/', trim($path, '/'));
$level = 0;
foreach ($parts as $part) {
switch ($part) {
case '.':
break;
case '..':
$level--;
if ($level < 0) {
return false;
}
break;
default:
$level++;
}
}
return true;
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->description;
}
/**
* {@inheritdoc}
*/
public function setDescription($description = null)
{
$this->description = $description;
return $this;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
return [
'id' => $this->getId(),
'name' => $this->getDisplayName(),
'size' => $this->getSize(),
'type' => $this->getMimeType(),
'etat' => $this->getEtat(),
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
'description' => $this->getDescription(),
'checksum' => $this->getChecksum(),
'icon' => MimeTypeIcon::getMimeTypeIcon($this->getMimeType()),
];
}
/**
* {@inheritdoc}
*/
public function getCreatedBy()
{
return $this->createdBy;
}
/**
* {@inheritdoc}
*/
public function getCreatedFrom()
{
return $this->createdFrom;
}
/**
* {@inheritdoc}
*/
public function setCreatedAt(DateTime $createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* {@inheritdoc}
*/
public function setCreatedBy($createdBy)
{
$this->createdBy = $createdBy;
return $this;
}
/**
* {@inheritdoc}
*/
public function setCreatedFrom($createdFrom)
{
$this->createdFrom = $createdFrom;
return $this;
}
/**
* {@inheritdoc}
*/
public function getLocalPath()
{
if (null !== $this->localTempPath) {
return $this->localTempPath;
}
$stream = $this->filesystem->createStream($this->getActualPath());
$stream->open(new StreamMode('rb'));
$handle = $stream->cast(STREAM_CAST_AS_STREAM);
if (false !== $handle) {
if (stream_is_local($handle)) {
$this->localTempPath = stream_get_meta_data($handle)['uri'];
fclose($handle);
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
return $this->localTempPath;
}
fclose($handle);
}
$this->localTempPath = tempnam(sys_get_temp_dir(), 'UploadedFile');
register_shutdown_function('unlink', $this->localTempPath);
$tmp = fopen($this->localTempPath, 'xb');
$this->copyTo($tmp);
fclose($tmp);
return $this->localTempPath;
}
/**
* Met à jour le chemin réel du fichier.
*
* @ORM\PostLoad
* @ORM\PostPersist
* @ORM\PostUpdate
*/
public function updateActualPath()
{
$this->actualPath = $this->path;
}
/**
* {@inheritdoc}
* Ne compare que la taille et la somme de contrôle : il existe une très faible probabilité que deux fichiers de
* même taille et même checksum soient différents.
*/
public function hasSameContent(UploadedFileInterface $other)
{
return $other->getSize() === $this->getSize() && $other->getChecksum() === $this->getChecksum();
}
}