file_save fails for remote streams on filesize()
| Project: | Drupal |
| Version: | 7.x-dev |
| Component: | file system |
| Category: | bug report |
| Priority: | critical |
| Assigned: | aaron |
| Status: | closed |
| Issue tags: | File API, stream wrappers |
Trying to create a stream wrapper class, at Media: YouTube. Making progress, except that when file_save() is called for the newly created file object, we receive a warning on
<?php
$file->filesize = filesize($file->uri);
?>Warning: filesize(): stat failed for youtube://v/-jubiv7QUco in file_save() (line 482 of /var/www/d7-media/includes/file.inc).
It looks like filesize() doesn't work with streams. This is obviously inconsistent with other stream wrapper file functions, as it apparently can't be overridden, and doesn't simply return FALSE (as does file_exists() or is_readable()).
I believe we should move the $file->filesize assignment outside this function, perhaps even to be handled by the stream wrappers somehow. Otherwise, the File API will fail for handling remote streams.

#1
Do we implement http://www.php.net/manual/en/streamwrapper.stream-stat.php? It seems so - maybe make sure you are returning some size in the array returned by stream_stat() from your implementation
#2
If that doesn't work, perhaps we should call stat() instead of filesize()
#3
No, I've implemented stream_stat(), but it still fails. Let me try using stat() instead of filesize().
#4
Hmm.. That fails too. Perhaps I'm implementing stream_stat incorrectly?
<?php/**
* Support for fstat().
*
* @return
* An array with file status, or FALSE in case of an error - see fstat()
* for a description of this array.
*/
public function stream_stat() {
return array(
'dev' => 0,
'ino' => 0,
'mode' => 0,
'nlink' => 0,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => 1000,
'atime' => 0,
'mtime' => 0,
'ctime' => 0,
'blksize' => 0,
'blocks' => 0,
);
}
?>
(I just have a fake value for size currently, obviously.)
#5
And my stat() implementation, for the record:
<?php
/**
* Save a file object to the database.
*
* If the $file->fid is not set a new record will be added. Re-saving an
* existing file will not change its status.
*
* @param $file
* A file object returned by file_load().
* @return
* The updated file object.
*
* @see hook_file_insert()
* @see hook_file_update()
*/
function file_save(stdClass $file) {
$file->timestamp = REQUEST_TIME;
$stat = stat($file->uri);
$file->filesize = $stat['size'];
if (empty($file->fid)) {
drupal_write_record('file', $file);
// Inform modules about the newly added file.
module_invoke_all('file_insert', $file);
}
else {
drupal_write_record('file', $file, 'fid');
// Inform modules that the file has been updated.
module_invoke_all('file_update', $file);
}
return $file;
}
?>
#6
from php.net re: stream_stat()
NOT stat().
see: http://www.php.net/manual/en/function.fstat.php
#7
@pwolanin: Unfortunately, that page also reads
I tried it anyway, and now get:
What if we call the stream wrapper's ->stream_stat() instead?
#8
oh wait, just saw that's expecting a handler. not sure if i can wrangle that, but i'll work on it this afternoon...
#9
OK, this takes care of the issue. We grab the stream and call ->stream_stat directly. Not sure why PHP isn't doing that natively; probably a PHP bug. Oh well.
#10
The last submitted patch failed testing.
#11
Odd indeed - how was this working even for local files like public://?
Quick look at the patch - looks reasonable.
#12
local files worked because it was calling filesave() directly on the filename, rather than the stream.
this patch fails now for normal uploads:
. unfortunately, i guess we'll need to switch on the stream type (drupallocal vs others).
#13
This one works fine with local and remote streams now.
#14
this patch works by opening the stream before reading stream_stat, then closing it. i believe that filesave works the same way for local files, so this shouldn't add anything substantially different functionally. i'm still baffled by stat() not working on the remote stream; maybe dopry can clue us someday into why that's not working as i expected.
#15
I have been using filesize() together with stream wrappers without problems, e.g. the stream wrapper included in the Services_Amazon_S3 PEAR package. I even have tests for it. The stream_stat() implementation is here.
Your stream_stat() should return an array similar to that returned by stat(). On my system, that array has both numerical and string keys:
<?phparray(26) {
[0]=>
int(769)
[1]=>
int(2606155)
[2]=>
int(33188)
[3]=>
int(1)
[4]=>
int(500)
[5]=>
int(1001)
[6]=>
int(0)
[7]=>
int(605)
[8]=>
int(1257696466)
[9]=>
int(1257696466)
[10]=>
int(1257696466)
[11]=>
int(4096)
[12]=>
int(8)
["dev"]=>
int(769)
["ino"]=>
int(2606155)
["mode"]=>
int(33188)
["nlink"]=>
int(1)
["uid"]=>
int(500)
["gid"]=>
int(1001)
["rdev"]=>
int(0)
["size"]=>
int(605)
["atime"]=>
int(1257696466)
["mtime"]=>
int(1257696466)
["ctime"]=>
int(1257696466)
["blksize"]=>
int(4096)
["blocks"]=>
int(8)
}
?>
Perhaps this is what's causing you trouble?
#16
huh. well, i used that nearly verbatim, and it still didn't work. note that http://us3.php.net/manual/en/function.filesize.php writes:
in fact, i'm still unable to get stat() working with my stream... :(
#17
OK, figured it out. I had to implement ->url_stat(). Really confusing. Not sure where ->stream_stat() is even used; i'd earlier interpreted its documentation to mean it was called for stat() functions.
#18
stream_stat() is used by fstat() (get info based on a filehandle) and url_stat() is used by stat() (get info based on a filename).
#19
Automatically closed -- issue fixed for 2 weeks with no activity.