|
|
@ -437,6 +437,9 @@ function islandora_view_datastream_deliver_chunks(AbstractDatastream $datastream |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Creates/returns the file URI for the content of a datastream for chunking. |
|
|
|
* Creates/returns the file URI for the content of a datastream for chunking. |
|
|
|
* |
|
|
|
* |
|
|
|
|
|
|
|
* File locks are used to ensure the datastream is completely downloaded before |
|
|
|
|
|
|
|
* attempting to serve up chunks from the file. |
|
|
|
|
|
|
|
* |
|
|
|
* @param AbstractDatastream $datastream |
|
|
|
* @param AbstractDatastream $datastream |
|
|
|
* An AbstractDatastream representing a datastream on a Fedora object. |
|
|
|
* An AbstractDatastream representing a datastream on a Fedora object. |
|
|
|
* |
|
|
|
* |
|
|
@ -451,44 +454,60 @@ function islandora_view_datastream_retrieve_file_uri(AbstractDatastream $datastr |
|
|
|
$file_uri = 'temporary://chunk_' . $datastream->parent->id . '_' . $datastream->id . '_' . $datastream->createdDate->getTimestamp() . '.' . $extension; |
|
|
|
$file_uri = 'temporary://chunk_' . $datastream->parent->id . '_' . $datastream->id . '_' . $datastream->createdDate->getTimestamp() . '.' . $extension; |
|
|
|
touch(drupal_realpath($file_uri)); |
|
|
|
touch(drupal_realpath($file_uri)); |
|
|
|
$fp = fopen($file_uri, 'r+b'); |
|
|
|
$fp = fopen($file_uri, 'r+b'); |
|
|
|
drupal_register_shutdown_function('fclose', $fp); |
|
|
|
|
|
|
|
if (flock($fp, LOCK_SH)) { |
|
|
|
if (flock($fp, LOCK_SH)) { |
|
|
|
if (feof($fp) && $datastream->size > 0) { |
|
|
|
try { |
|
|
|
// Just opened at beginning of file, if beginning == EOF, need to grab it. |
|
|
|
if (feof($fp) && $datastream->size > 0) { |
|
|
|
if (flock($fp, LOCK_EX)) { |
|
|
|
// Just opened at beginning of file, if beginning == EOF, need to grab |
|
|
|
// Upgrade to exclusive lock, write file. |
|
|
|
// it. |
|
|
|
$file = islandora_temp_file_entry($file_uri, $datastream->mimeType); |
|
|
|
if (!flock($fp, LOCK_EX | LOCK_NB)) { |
|
|
|
if ($file->filesize == $datastream->size) { |
|
|
|
// Hypothetically, two threads could have a "shared" lock with an |
|
|
|
// Populated in another thread; downgrade lock and return. |
|
|
|
// unpopulated file, so to avoid deadlock on the "exclusive" lock, |
|
|
|
flock($fp, LOCK_SH); |
|
|
|
// drop the "shared" lock before blocking to obtain the "exclusive" |
|
|
|
return $file_uri; |
|
|
|
// lock. |
|
|
|
|
|
|
|
flock($fp, LOCK_UN); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (flock($fp, LOCK_EX)) { |
|
|
|
|
|
|
|
// Get exclusive lock, write file. |
|
|
|
|
|
|
|
$file = islandora_temp_file_entry($file_uri, $datastream->mimeType); |
|
|
|
|
|
|
|
if ($file->filesize == $datastream->size) { |
|
|
|
|
|
|
|
// Populated in another thread while we were waiting for the |
|
|
|
|
|
|
|
// "exclusive" lock; drop lock and return. |
|
|
|
|
|
|
|
flock($fp, LOCK_UN); |
|
|
|
|
|
|
|
fclose($fp); |
|
|
|
|
|
|
|
return $file_uri; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
$datastream->getContent($file->uri); |
|
|
|
$datastream->getContent($file->uri); |
|
|
|
$file = file_save($file); |
|
|
|
$file = file_save($file); |
|
|
|
if ($file->filesize != $datastream->size) { |
|
|
|
if ($file->filesize != $datastream->size) { |
|
|
|
throw new RepositoryException(t('Size of file downloaded for chunking does not match: Got @apparent bytes when expecting @actual.', array( |
|
|
|
throw new RepositoryException(t('Size of file downloaded for chunking does not match: Got @apparent bytes when expecting @actual.', array( |
|
|
|
'@apparent' => $file->filesize, |
|
|
|
'@apparent' => $file->filesize, |
|
|
|
'@actual' => $datastream->size, |
|
|
|
'@actual' => $datastream->size, |
|
|
|
))); |
|
|
|
))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (RepositoryException $e) { |
|
|
|
|
|
|
|
file_delete($file); |
|
|
|
|
|
|
|
throw $e; |
|
|
|
} |
|
|
|
} |
|
|
|
// Downgrade to shared lock. |
|
|
|
|
|
|
|
flock($fp, LOCK_SH); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
catch (RepositoryException $e) { |
|
|
|
else { |
|
|
|
file_delete($file); |
|
|
|
throw new Exception(t('Failed to acquire write lock when downloading @pid/@dsid for chunking.', array( |
|
|
|
throw $e; |
|
|
|
'@pid' => $datastream->parent->id, |
|
|
|
|
|
|
|
'@dsid' => $datastream->id, |
|
|
|
|
|
|
|
))); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
flock($fp, LOCK_UN); |
|
|
|
throw new Exception(t('Failed to acquire write lock when downloading @pid/@dsid for chunking.', array( |
|
|
|
fclose($fp); |
|
|
|
'@pid' => $datastream->parent->id, |
|
|
|
return $file_uri; |
|
|
|
'@dsid' => $datastream->id, |
|
|
|
} |
|
|
|
))); |
|
|
|
catch (Exception $e) { |
|
|
|
} |
|
|
|
flock($fp, LOCK_UN); |
|
|
|
|
|
|
|
fclose($fp); |
|
|
|
|
|
|
|
throw $e; |
|
|
|
} |
|
|
|
} |
|
|
|
return $file_uri; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
throw new Exception(t('Failed to acquire shared lock when chunking @pid/@dsid.', array( |
|
|
|
throw new Exception(t('Failed to acquire shared lock when chunking @pid/@dsid.', array( |
|
|
|
'@pid' => $datastream->parent->id, |
|
|
|
'@pid' => $datastream->parent->id, |
|
|
|