Merge branch 'mediafile_fromfilehandle_avatar' into 'master'

MediaFile::fromFilehandle on profile avatar upload

This is mainly because I in GNU social want to change the constructor of
MediaFile and thus use fromFilehandle in Qvitter, so we don't have to sync
the changes as much.

This also will make sure that uploaded files can be reused if they match a
SHA256 hash of the avatar we want to set. Parts of my rewriting also provide
a pretty good example of how we can reuse existing File entries as Avatar sources.

See merge request !34
This commit is contained in:
hannes 2016-03-05 04:57:46 -05:00
commit 68b3d377e8
3 changed files with 92 additions and 89 deletions

View File

@ -78,44 +78,27 @@ class ApiAccountUpdateProfileBannerAction extends ApiAuthAction
protected function handle() protected function handle()
{ {
parent::handle(); parent::handle();
$profile = $this->user->getProfile();
// see if we have regular uploaded image data // see if we have regular uploaded image data
try { try {
$mediafile = MediaFile::fromUpload('banner', $profile); $mediafile = MediaFile::fromUpload('banner', $this->scoped);
} catch (NoUploadedMediaException $e) { } catch (NoUploadedMediaException $e) {
// if not we may have base64 data // if not we may have base64 data
$img = $this->img;
if(stristr($img, 'image/jpeg')) {
$img_mime = 'image/jpeg';
}
elseif(stristr($img, 'image/png')) {
// should convert to jpg here!!
$img_mime = 'image/png';
}
// i don't remember why we had to do this $this->img = str_replace('data:image/jpeg;base64,', '', $this->img);
$img = str_replace('data:image/jpeg;base64,', '', $img); $this->img = str_replace('data:image/png;base64,', '', $this->img);
$img = str_replace('data:image/png;base64,', '', $img); $this->img = str_replace(' ', '+', $this->img);
$img = str_replace(' ', '+', $img); $this->img = base64_decode($this->img, true);
$img = base64_decode($img, true);
try { $fh = tmpfile();
$img_filename = File::filename($profile, 'cover', $img_mime); fwrite($fh, $this->img);
$img_path = File::path($img_filename); unset($this->img);
$img_success = file_put_contents($img_path, $img); fseek($fh, 0);
$img_mimetype = MediaFile::getUploadedMimeType($img_path, $img_filename);
$mediafile = new MediaFile($profile, $img_filename, $img_mimetype);
} catch (Exception $e) {
$this->clientError($e, 400);
}
}
if(!$mediafile instanceof MediaFile) { $mediafile = MediaFile::fromFilehandle($fh, $this->scoped);
$this->clientError(_('Could not process image data.'), 400);
} }
// maybe resize // maybe resize
@ -132,16 +115,27 @@ class ApiAccountUpdateProfileBannerAction extends ApiAuthAction
// crop // crop
try { try {
$imagefile = new ImageFile($mediafile->fileRecord->id, File::path($mediafile->filename)); $imagefile = ImageFile::fromFileObject($mediafile->fileRecord);
$imagefile->resizeTo(File::path($mediafile->filename), array('width'=>$width, 'height'=>$height, 'x'=>$this->cropX, 'y'=>$this->cropY, 'w'=>$this->cropW, 'h'=>$this->cropH)); unset($mediafile);
$result['url'] = File::url($mediafile->filename);
// We're just using the Avatar function to build a filename here
// but we don't save it _as_ an avatar below... but in the same dir!
$filename = Avatar::filename(
$this->scoped->getID(),
image_type_to_extension($imagefile->preferredType()),
null,
'banner-'.common_timestamp()
);
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$width, 'height'=>$height, 'x'=>$this->cropX, 'y'=>$this->cropY, 'w'=>$this->cropW, 'h'=>$this->cropH));
$result['url'] = Avatar::url($filename);
} catch (Exception $e) { } catch (Exception $e) {
$this->clientError(_('The image could not be resized and cropped. '.$e), 422); $this->clientError(_('The image could not be resized and cropped. '.$e), 422);
} }
// save in profile_prefs // save in profile_prefs
try { try {
Profile_prefs::setData($profile, 'qvitter', 'cover_photo', $result['url']); Profile_prefs::setData($this->scoped, 'qvitter', 'cover_photo', $result['url']);
} catch (ServerException $e) { } catch (ServerException $e) {
$this->clientError(_('The image could not be resized and cropped. '.$e), 422); $this->clientError(_('The image could not be resized and cropped. '.$e), 422);
} }

View File

@ -67,6 +67,15 @@ class ApiUpdateAvatarAction extends ApiAuthAction
$this->cropY = $this->trimmed('cropY'); $this->cropY = $this->trimmed('cropY');
$this->img = $this->trimmed('img'); $this->img = $this->trimmed('img');
$this->img = str_replace('data:image/jpeg;base64,', '', $this->img);
$this->img = str_replace('data:image/png;base64,', '', $this->img);
$this->img = str_replace(' ', '+', $this->img);
$this->img = base64_decode($this->img);
if (empty($this->img)) {
throw new ClientException(_('No uploaded image data.'));
}
return true; return true;
} }
@ -79,47 +88,34 @@ class ApiUpdateAvatarAction extends ApiAuthAction
{ {
parent::handle(); parent::handle();
$profile = $this->user->getProfile(); $imagefile = null;
$base64img = $this->img; // write the image to a temporary file
if(stristr($base64img, 'image/jpeg')) { $fh = tmpfile();
$base64img_mime = 'image/jpeg'; fwrite($fh, $this->img);
} unset($this->img); // no need to keep it in memory
elseif(stristr($base64img, 'image/png')) { // seek back to position 0, so we don't read EOF directly
// should convert to jpg here!! fseek($fh, 0);
$base64img_mime = 'image/png'; // read the temporary file as an uploaded image, will store File object
} $mediafile = MediaFile::fromFilehandle($fh, $this->scoped);
$base64img = str_replace('data:image/jpeg;base64,', '', $base64img); // Deletes the temporary file, if it was needed we stored it in fromFilehandle
$base64img = str_replace('data:image/png;base64,', '', $base64img); fclose($fh);
$base64img = str_replace(' ', '+', $base64img);
$base64img_hash = md5($base64img);
$base64img = base64_decode($base64img);
$base64img_basename = basename('avatar');
$base64img_filename = File::filename($profile, $base64img_basename, $base64img_mime);
$base64img_path = File::path($base64img_filename);
$base64img_success = file_put_contents($base64img_path, $base64img);
$base64img_mimetype = MediaFile::getUploadedMimeType($base64img_path, $base64img_filename);
$mediafile = new MediaFile($profile, $base64img_filename, $base64img_mimetype);
$imagefile = new ImageFile($mediafile->fileRecord->id, File::path($mediafile->filename));
$imagefile->resizeTo(File::path($mediafile->filename), array('width'=>$this->cropW, 'height'=>$this->cropH, 'x'=>$this->cropX, 'y'=>$this->cropY, 'w'=>$this->cropW, 'h'=>$this->cropH));
$type = $imagefile->preferredType(); // Now try to get it as an ImageFile since it has some handy functions
$imagefile = ImageFile::fromFileObject($mediafile->fileRecord);
unset($mediafile); // This isn't needed in memory.
// Get an appropriate filename for the avatar
$filename = Avatar::filename( $filename = Avatar::filename(
$profile->id, $this->scoped->getID(),
image_type_to_extension($type), image_type_to_extension($imagefile->preferredType()),
null, null,
common_timestamp() common_timestamp()
); );
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$this->cropW, 'height'=>$this->cropH, 'x'=>$this->cropX, 'y'=>$this->cropY, 'w'=>$this->cropW, 'h'=>$this->cropH));
$filepath = Avatar::path($filename); $this->scoped->setOriginal($filename);
$imagefile->copyTo($filepath); $twitter_user = $this->twitterUserArray($this->scoped, true);
$profile = $this->user->getProfile();
$profile->setOriginal($filename);
$mediafile->delete();
$twitter_user = $this->twitterUserArray($profile, true);
$this->initDocument('json'); $this->initDocument('json');
$this->showJsonObjects($twitter_user); $this->showJsonObjects($twitter_user);
$this->endDocument('json'); $this->endDocument('json');

View File

@ -67,6 +67,15 @@ class ApiUpdateBackgroundImageAction extends ApiAuthAction
$this->cropY = $this->trimmed('cropY'); $this->cropY = $this->trimmed('cropY');
$this->img = $this->trimmed('img'); $this->img = $this->trimmed('img');
$this->img = str_replace('data:image/jpeg;base64,', '', $this->img);
$this->img = str_replace('data:image/png;base64,', '', $this->img);
$this->img = str_replace(' ', '+', $this->img);
$this->img = base64_decode($this->img);
if (empty($this->img)) {
throw new ClientException(_('No uploaded image data.'));
}
return true; return true;
} }
@ -79,31 +88,35 @@ class ApiUpdateBackgroundImageAction extends ApiAuthAction
{ {
parent::handle(); parent::handle();
$profile = $this->user->getProfile(); $imagefile = null;
$base64img = $this->img;
if(stristr($base64img, 'image/jpeg')) {
$base64img_mime = 'image/jpeg';
}
elseif(stristr($base64img, 'image/png')) {
// should convert to jpg here!!
$base64img_mime = 'image/png';
}
$base64img = str_replace('data:image/jpeg;base64,', '', $base64img);
$base64img = str_replace('data:image/png;base64,', '', $base64img);
$base64img = str_replace(' ', '+', $base64img);
$base64img_hash = md5($base64img);
$base64img = base64_decode($base64img);
$base64img_basename = basename('bg');
$base64img_filename = File::filename($profile, $base64img_basename, $base64img_mime);
$base64img_path = File::path($base64img_filename);
$base64img_success = file_put_contents($base64img_path, $base64img);
$base64img_mimetype = MediaFile::getUploadedMimeType($base64img_path, $base64img_filename);
$mediafile = new MediaFile($profile, $base64img_filename, $base64img_mimetype);
$imagefile = new ImageFile($mediafile->fileRecord->id, File::path($mediafile->filename));
$imagefile->resizeTo(File::path($mediafile->filename), array('width'=>1280, 'height'=>1280, 'x'=>$this->cropX, 'y'=>$this->cropY, 'w'=>$this->cropW, 'h'=>$this->cropH));
$result['url'] = File::url($mediafile->filename);
Profile_prefs::setData($profile, 'qvitter', 'background_image', $result['url']); // put the image data in a temporary file
$fh = tmpfile();
fwrite($fh, $this->img);
unset($this->img);
fseek($fh, 0); // go to beginning just to be sure the content is read properly
// We get a MediaFile with a File object using the filehandle
$mediafile = MediaFile::fromFilehandle($fh, $this->scoped);
// and can dispose of the temporary filehandle since we're certain we have a File on disk now
fclose($fh);
$imagefile = ImageFile::fromFileObject($mediafile->fileRecord);
unset($mediafile); // No need to keep the MediaFile around anymore, everything we need is in ImageFile
// We're just using the Avatar function to build a filename here
// but we don't save it _as_ an avatar below... but in the same dir!
$filename = Avatar::filename(
$this->scoped->getID(),
image_type_to_extension($imagefile->preferredType()),
null,
'bg-'.common_timestamp()
);
$imagefile->resizeTo(Avatar::path($filename), array('width'=>1280, 'height'=>1280, 'x'=>$this->cropX, 'y'=>$this->cropY, 'w'=>$this->cropW, 'h'=>$this->cropH));
$result['url'] = Avatar::url($filename);
Profile_prefs::setData($this->scoped, 'qvitter', 'background_image', $result['url']);
$this->initDocument('json'); $this->initDocument('json');
$this->showJsonObjects($result); $this->showJsonObjects($result);