Is it possible to customize the chunk configuration in Filepond such that the chunk information is provided to the upload server:
as query parameters instead of headers
with custom query parameter names instead of Upload-Length, Upload-Name, and Upload-Offset
I am trying to fit Filepond's chunk implementation to a third party upload endpoint that I don't have control over.
I have found the Advanced configuration where you provide a process function which I've played with a little bit to see what comes through the options param -- however that appears (I think) to make the chunking calculations my responsibility. My original thought was to manipulate the options.chunkServer.url to include the query params I need but I don't believe this processes individual chunks.
In case it makes a difference, this is being done in React using the react-filepond package.
I made and implementation in Laravel 6 using Traits and some "bad practices" (I didn't have time because ... release in prod) to join chunks into a file
Basically:
post to get unique id folder to storage
get chunks and join together
profit!
Here's the full code:
<?php
namespace App\Http\Traits\Upload;
use Closure;
use Faker\Factory as Faker;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
trait Uploadeable
{
public function uploadFileInStorage(Request $request, Closure $closure)
{
// get the nex offset for next chunk send
if (($request->isMethod('options') || $request->isMethod('head')) && $request->has('patch')) {
//get the temp dir
$dir = $request->patch . DIRECTORY_SEPARATOR;
// reead all chunks in directory
$patch = collect(Storage::files($dir))
->sortBy(function ($file) {
return Storage::lastModified($file);
});
// read offsets for calculate
$offsets = array();
$size = 0;
$last_offset = 0;
foreach ($patch as $filename) {
$size = Storage::size($filename);
list($dir, $offset) = explode('file.patch.', $filename, 2);
array_push($offsets, $offset);
if ($offset > 0 && !in_array($offset - $size, $offsets)) {
$last_offset = $offset - $size;
break;
}
// last offset is at least next offset
$last_offset = $offset + $size;
}
// return offset
return response($last_offset, 200)
->header('Upload-Offset', $last_offset);
}
// chunks
if ($request->isMethod('patch') && $request->has('patch')) {
// get the temp dir
$dir = $request->patch . DIRECTORY_SEPARATOR;
// read headers
$offset = $request->header('upload-offset');
$length = $request->header('upload-length');
// should be numeric values, else exit
if (!is_numeric($offset) || !is_numeric($length)) {
return response('', 400);
}
// get the file name
$name = $request->header('Upload-Name');
// sleep server for get a diference between file created to sort
usleep(500000);
// storage the chunk with name + offset
Storage::put($dir . 'file.patch.' . $offset, $request->getContent());
// calculate total size of patches
$size = 0;
$patch = Storage::files($dir);
foreach ($patch as $filename) {
$size += Storage::size($filename);
}
// make the final file
if ($size == $length) {
// read all chunks in directory
$files = collect(Storage::files($dir))
->sortBy(function ($file) {
return Storage::lastModified($file);
});
// create output file
//Log::info(storage_path('app'));
$new_file_name = $final_name = trim(storage_path('app') . DIRECTORY_SEPARATOR . $dir . $name);
$file_handle = fopen($new_file_name, 'w');
// write patches to file
foreach ($files as $filename) {
// get offset from filename
list($dir, $offset) = explode('.patch.', $filename, 2);
// read chunk
$patch_handle = fopen(storage_path('app') . DIRECTORY_SEPARATOR . trim($filename), 'r');
$patch_contents = fread($patch_handle, filesize(storage_path('app') . DIRECTORY_SEPARATOR . trim($filename)));
fclose($patch_handle);
// apply patch
fseek($file_handle, $offset);
fwrite($file_handle, $patch_contents);
}
// done with file
fclose($file_handle);
// file permission (prefered 0755)
chmod($final_name, 0777);
// remove patches
foreach ($patch as $filename) {
$new_file_name = storage_path('app') . DIRECTORY_SEPARATOR . trim($filename);
unlink($new_file_name);
}
// simple class (no time to explain)
$file = new UploadedFile(
$final_name,
basename($final_name),
mime_content_type($final_name),
filesize($final_name),
false
);
$dir = $request->patch . DIRECTORY_SEPARATOR;
$object = new \stdClass();
$object->full_path = (string)$file->getPathname();
$object->directory = (string)($dir);
$object->path = (string)($dir . basename($final_name));
$object->name = (string)$file->getClientOriginalName();
$object->mime_type = (string)$file->getClientMimeType();
$object->extension = (string)$file->getExtension();
$object->size = (string)$this->formatSizeUnits($file->getSize());
// exec closure
$closure($file, (object)$object, $request);
}
// response
return response()->json([
'message' => 'Archivo subido correctamente.',
'filename' => $name
], 200);
}
// get dir unique id folder temp
if ($request->isMethod('post')) {
$faker = Faker::create();
$unique_id = $faker->uuid . '-' . time();
$unique_folder_path = $unique_id;
// create directory
Storage::makeDirectory($unique_folder_path);
// permisos directorio
chmod(storage_path('app') . DIRECTORY_SEPARATOR . $unique_folder_path . DIRECTORY_SEPARATOR, 0777);
// response with folder
return response($unique_id, 200)
->header('Content-Type', 'text/plain');
}
}
private function formatSizeUnits($bytes)
{
if ($bytes >= 1073741824) {
$bytes = number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
$bytes = number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
$bytes = number_format($bytes / 1024, 2) . ' KB';
} elseif ($bytes > 1) {
$bytes = $bytes . ' bytes';
} elseif ($bytes == 1) {
$bytes = $bytes . ' byte';
} else {
$bytes = '0 bytes';
}
return $bytes;
}
}
ยดยดยด
I have this problem with GD image processing in Opencart that creates real bad blurry images after resize. Nothing I have tried so far has helped.
Below is the code for the image.php
<?php
class Image {
private $file;
private $image;
private $info;
public function __construct($file) {
if (file_exists($file)) {
$this->file = $file;
$info = getimagesize($file);
$this->info = array(
'width' => $info[0],
'height' => $info[1],
'bits' => $info['bits'],
'mime' => $info['mime']
);
$this->image = $this->create($file);
} else {
exit('Error: Could not load image ' . $file . '!');
}
}
private function create($image) {
$mime = $this->info['mime'];
if ($mime == 'image/gif') {
return imagecreatefromgif($image);
} elseif ($mime == 'image/png') {
return imagecreatefrompng($image);
} elseif ($mime == 'image/jpeg') {
return imagecreatefromjpeg($image);
}
}
public function save($file, $quality = 100) {
$info = pathinfo($file);
$extension = strtolower($info['extension']);
if (is_resource($this->image)) {
if ($extension == 'jpeg' || $extension == 'jpg') {
imagejpeg($this->image, $file, $quality);
} elseif($extension == 'png') {
imagepng($this->image, $file);
} elseif($extension == 'gif') {
imagegif($this->image, $file);
}
imagedestroy($this->image);
}
}
/**
*
* #param width
* #param height
* #param default char [default, w, h]
* default = scale with white space,
* w = fill according to width,
* h = fill according to height
*
*/
public function resize($width = 0, $height = 0, $default = '') {
if (!$this->info['width'] || !$this->info['height']) {
return;
}
$xpos = 0;
$ypos = 0;
$scale = 1;
$scale_w = $width / $this->info['width'];
$scale_h = $height / $this->info['height'];
if ($default == 'w') {
$scale = $scale_w;
} elseif ($default == 'h'){
$scale = $scale_h;
} else {
$scale = min($scale_w, $scale_h);
}
if ($scale == 1 && $scale_h == $scale_w && $this->info['mime'] != 'image/png')
{
return;
}
$new_width = (int)($this->info['width'] * $scale);
$new_height = (int)($this->info['height'] * $scale);
$xpos = (int)(($width - $new_width) / 2);
$ypos = (int)(($height - $new_height) / 2);
$image_old = $this->image;
$this->image = imagecreatetruecolor($width, $height);
if (isset($this->info['mime']) && $this->info['mime'] == 'image/png') {
imagealphablending($this->image, false);
imagesavealpha($this->image, true);
$background = imagecolorallocatealpha($this->image, 255, 255, 255, 127);
imagecolortransparent($this->image, $background);
} else {
$background = imagecolorallocate($this->image, 255, 255, 255);
}
imagefilledrectangle($this->image, 0, 0, $width, $height, $background);
imagecopyresampled($this->image, $image_old, $xpos, $ypos, 0, 0, $new_width,
$new_height, $this->info['width'], $this->info['height']);
imagedestroy($image_old);
$this->info['width'] = $width;
$this->info['height'] = $height;
}
public function watermark($file, $position = 'bottomright') {
$watermark = $this->create($file);
$watermark_width = imagesx($watermark);
$watermark_height = imagesy($watermark);
switch($position) {
case 'topleft':
$watermark_pos_x = 0;
$watermark_pos_y = 0;
break;
case 'topright':
$watermark_pos_x = $this->info['width'] - $watermark_width;
$watermark_pos_y = 0;
break;
case 'bottomleft':
$watermark_pos_x = 0;
$watermark_pos_y = $this->info['height'] - $watermark_height;
break;
case 'bottomright':
$watermark_pos_x = $this->info['width'] - $watermark_width;
$watermark_pos_y = $this->info['height'] - $watermark_height;
break;
}
imagecopy($this->image, $watermark,
$watermark_pos_x, $watermark_pos_y, 0, 0, 120, 40);
imagedestroy($watermark);
}
public function crop($top_x, $top_y, $bottom_x, $bottom_y) {
$image_old = $this->image;
$this->image = imagecreatetruecolor($bottom_x - $top_x, $bottom_y - $top_y);
imagecopy($this->image, $image_old, 0, 0, $top_x, $top_y,
$this->info['width'], $this->info['height']);
imagedestroy($image_old);
$this->info['width'] = $bottom_x - $top_x;
$this->info['height'] = $bottom_y - $top_y;
}
public function rotate($degree, $color = 'FFFFFF') {
$rgb = $this->html2rgb($color);
$this->image = imagerotate($this->image, $degree,
imagecolorallocate($this->image, $rgb[0], $rgb[1], $rgb[2]));
$this->info['width'] = imagesx($this->image);
$this->info['height'] = imagesy($this->image);
}
private function filter($filter) {
imagefilter($this->image, $filter);
}
private function text($text, $x = 0, $y = 0, $size = 5, $color = '000000') {
$rgb = $this->html2rgb($color);
imagestring($this->image, $size, $x, $y, $text,
imagecolorallocate($this->image, $rgb[0], $rgb[1], $rgb[2]));
}
private function merge($file, $x = 0, $y = 0, $opacity = 100) {
$merge = $this->create($file);
$merge_width = imagesx($image);
$merge_height = imagesy($image);
imagecopymerge($this->image, $merge, $x, $y, 0, 0, $merge_width,
$merge_height, $opacity);
}
private function html2rgb($color) {
if ($color[0] == '#') {
$color = substr($color, 1);
}
if (strlen($color) == 6) {
list($r, $g, $b) = array($color[0] . $color[1], $color[2] . $color[3],
$color[4] . $color[5]);
} elseif (strlen($color) == 3) {
list($r, $g, $b) = array($color[0] . $color[0], $color[1] . $color[1],
$color[2] . $color[2]);
} else {
return false;
}
$r = hexdec($r);
$g = hexdec($g);
$b = hexdec($b);
return array($r, $g, $b);
}
}
?>
As you can see the quality is already set to 100 , so that doesn't help.
I have tried replacing resize with resample - but that produced no visible result.
However I have found this suggestion (code below) to sharpen images, unfortunately I am not sure how and where to use it. Especially since original code processed multiple image types. Please help to put this in the right place.
{
$matrix = array(
array(-1, -1, -1),
array(-1, 16, -1),
array(-1, -1, -1),
);
$divisor = array_sum(array_map('array_sum', $matrix));
$offset = 0;
imageconvolution($image, $matrix, $divisor, $offset);
return $image;
}
Also, if you have other suggestions to improve this code, help is greatly appreciated! I think that goes for the whole Opencart community, as this has been discussed many time but no working solution posted as of yet.
The quality parameter will only be applicable to .jpeg images.
To sharpen the images you could apply the imageconvolution() code within the resize() function.
public function resize($width = 0, $height = 0, $default = '') {
if (!$this->info['width'] || !$this->info['height']) {
return;
}
$xpos = 0;
$ypos = 0;
$scale = 1;
$scale_w = $width / $this->info['width'];
$scale_h = $height / $this->info['height'];
if ($default == 'w') {
$scale = $scale_w;
} elseif ($default == 'h') {
$scale = $scale_h;
} else {
$scale = min($scale_w, $scale_h);
}
if ($scale == 1 && $scale_h == $scale_w && $this->info['mime'] != 'image/png') {
return;
}
$new_width = (int)($this->info['width'] * $scale);
$new_height = (int)($this->info['height'] * $scale);
$xpos = (int)(($width - $new_width) / 2);
$ypos = (int)(($height - $new_height) / 2);
$image_old = $this->image;
$this->image = imagecreatetruecolor($width, $height);
if (isset($this->info['mime']) && $this->info['mime'] == 'image/png') {
imagealphablending($this->image, false);
imagesavealpha($this->image, true);
$background = imagecolorallocatealpha($this->image, 255, 255, 255, 127);
imagecolortransparent($this->image, $background);
} else {
$background = imagecolorallocate($this->image, 255, 255, 255);
}
imagefilledrectangle($this->image, 0, 0, $width, $height, $background);
imagecopyresampled($this->image, $image_old, $xpos, $ypos, 0, 0, $new_width, $new_height, $this->info['width'], $this->info['height']);
//Image Sharpening Code
$matrix = array(
array(0.0, -1.0, 0.0),
array(-1.0, 5.0, -1.0),
array(0.0, -1.0, 0.0)
);
$divisor = array_sum(array_map('array_sum', $matrix));
$offset = 0;
imageconvolution($this->image, $matrix, $divisor, $offset);
// End Image Sharpening Code
imagedestroy($image_old);
$this->info['width'] = $width;
$this->info['height'] = $height;
}
References:
sharpen image quality with PHP gd library
http://adamhopkinson.co.uk/blog/2010/08/26/sharpen-an-image-using-php-and-gd/
I try use this:
$image = new JImage();
$image->loadFile($item->logo);
$image->resize('208', '125');
$properties = JImage::getImageFileProperties($item->logo);
echo $image->toFile(JPATH_CACHE . DS . $item->logo, $properties->type);
But not work =\ any idea?
Try this out:
// Set the path to the file
$file = '/Absolute/Path/To/File';
// Instantiate our JImage object
$image = new JImage($file);
// Get the file's properties
$properties = JImage::getImageFileProperties($file);
// Declare the size of our new image
$width = 100;
$height = 100;
// Resize the file as a new object
$resizedImage = $image->resize($width, $height, true);
// Determine the MIME of the original file to get the proper type for output
$mime = $properties->mime;
if ($mime == 'image/jpeg')
{
$type = IMAGETYPE_JPEG;
}
elseif ($mime == 'image/png')
{
$type = IMAGETYPE_PNG;
}
elseif ($mime == 'image/gif')
{
$type = IMAGETYPE_GIF;
}
// Store the resized image to a new file
$resizedImage->toFile('/Absolute/Path/To/New/File', $type);
When I resize an image keeping $config['create_thumb'] = true Codeigniter adds the string "_thumb" in the end of the name of resized image.
What I want ask is, is it possible to add the suffix "_thumb" in the beginning of the image name
example:
original_name.jpg
after resizing:
original_name_thumb.jpg
What I want:
thumb_original_name.jpg
Any kind of help is appreciated!
You can try this:
$data = $this->upload->data();
$config['create_thumb'] = false;
$config['new_image'] = 'thumb_'.$data['file_name'];
And if you have other problems with CI image manipulation library, maybe you can try ImageMoo.
If u don't want to add the suffix _thumb in thumbnail file name then
use $config['thumb_marker'] = false;
this can be achieved by following way
$data = $this->upload->data();
$config['create_thumb'] = false;
$config['new_image'] = 'thumb_'.$data['file_name'];
for more information about re-size image check out following Code.
function special_resize($oldFile, $width, $height)
{
$obj =& get_instance();
$info = pathinfo($oldFile);
out($info);
$tempFile = '/public/files/files/temp/' . $info['filename'] . '-' . $width . 'x' . $height . '.' . $info['extension'];
$newFile = '/public/files/files/cache/' . $info['filename'] . '-' . $width . 'x' . $height . '.' . $info['extension'];
//if image already exists, use it
if(file_exists('.' .$newFile))
return $newFile;
//math for resize/crop without loss
$o_size = _get_size( '/' . $info['dirname'] . '/' . $info['basename']);
$master_dim = ($o_size['width']-$width < $o_size['height']-$height?'width':'height');
$perc = max( (100*$width)/$o_size['width'] , (100*$height)/$o_size['height'] );
$perc = round($perc, 0);
$w_d = round(($perc*$o_size['width'])/100, 0);
$h_d = round(($perc*$o_size['height'])/100, 0);
// end math stuff
/*
* Resize image
*/
$config['image_library'] = 'gd2';
$config['source_image'] = $oldFile;
$config['new_image'] = '.' . $tempFile;
$config['maintain_ratio'] = TRUE;
$config['master_dim'] = $master_dim;
$config['width'] = $w_d + 1;
$config['height'] = $h_d + 1;
$obj->image_lib->initialize($config);
$obj->image_lib->resize();
$size = _get_size($tempFile);
unset($config); // clear $config
/*
* Crop image in weight, height
*/
$config['image_library'] = 'gd2';
$config['source_image'] = '.' . $tempFile;
$config['new_image'] = '.' . $newFile;
$config['maintain_ratio'] = FALSE;
$config['width'] = $width;
$config['height'] = $height;
$config['y_axis'] = round(($size['height'] - $height) / 2);
$config['x_axis'] = 0;
$obj->image_lib->clear();
$obj->image_lib->initialize($config);
if ( ! $obj->image_lib->crop())
{
echo $obj->image_lib->display_errors();
}
$info = pathinfo($newFile);
out($info);
return $newFile;
}
I think this function isn't as efficient as it should be. I'd appreciate some suggestions on how I can structure it to be faster and take less memory. This is what the code does:
checks to see if the image was uploaded
add details about it (tags, name, details) to the database
if the variable $orientation was set, rotate the image
if the image is wider than 600px, resize it
create a thumbnail
I think the inefficiencies come from having steps 3,4,5 all separate. Is there any way to consolidate them? Thanks!
function insert()
{
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'jpg';
$config['max_size'] = '5000';
$config['max_width'] = '4096';
$config['max_height'] = '4096';
$this->load->library('upload', $config);
if (!$this->upload->do_upload())
{
$data = array('error' => $this->upload->display_errors());
$data['title'] = "Add Photo | Mark The Dark";
$this->load->view('photo_add_view', $data);
}
else
{
//get uploaded image info
$data = array('upload_data' => $this->upload->data());
//clean the data
$data = $this->input->xss_clean($data);
//get orientation info and erase it from POST variable
//$orientation = $_POST['orientation'];
//unset($_POST['orientation']);
//grab the tags
$tags = $_POST['tags'];
unset($_POST['tags']);
//add in some other stuff
$_POST['time'] = date('YmdHis');
$_POST['author'] = $this->dx_auth->get_user_id();
//insert it in the database
$this->db->insert('photos', $_POST);
$photo_id = $this->db->insert_id();
//add stuff to tags table
/*
$tags_array = preg_split('/[\s,;]+/', $tags);
foreach($tags_array as $tag)
{
if($tag != "" || $tag != null)
$this->db->insert('tags', array('id' => $photo_id, 'word' => $tag));
}*/
//CXtags
/*$tags_array = preg_split('/[\s,;]+/', $tags);
foreach($tags_array as $tag)
{
if($tag == "" || $tag == null)
{unset($tags_array[$tag]);}
}
*/
$tags_array = $this->CXTags->comma_to_array($tags);
foreach($tags_array as $tag)
{$tags_array[$tag] = $this->CXTags->make_safe_tag($tag);}
$topass = array(
'table' => 'photos',
'tags' => $tags_array,
'row_id' => $photo_id,
'user_id' => $_POST['author']
);
$this->CXTags->add_tags($topass);
//rename the file to the id of the record in the database
rename("./uploads/" . $data['upload_data']['file_name'], "./uploads/" . $photo_id . ".jpg");
list($width, $height, $type, $attr) = getimagesize("./uploads/" . $photo_id . '.jpg');
if (($orientation == 1) || ($orientation == 2))
{
//echo $orientation;
//rotate image
$config['image_library'] = 'GD2';
$config['source_image'] = './uploads/' . $photo_id . '.jpg';
if ($orientation == 1)
{
$config['rotation_angle'] = 270;
}
elseif ($orientation == 2)
{
$config['rotation_angle'] = 90;
}
$this->load->library('image_lib', $config);
$this->image_lib->initialize($config);
if(!$this->image_lib->rotate())
{
echo $this->image_lib->display_errors();
}
}
$this->load->library('image_lib');
if ($width > 600)
{
//resize image
$config['image_library'] = 'GD2';
$config['source_image'] = './uploads/' . $photo_id . '.jpg';
$config['new_image'] = './uploads/photos/' . $photo_id . '.jpg';
$config['create_thumb'] = FALSE;
$config['maintain_ratio'] = TRUE;
$config['width'] = 600;//180
$config['height'] = 480;
$config['master_dim'] = 'width';
$this->image_lib->initialize($config);
$this->load->library('image_lib', $config);
if(!$this->image_lib->resize())
{
echo $this->image_lib->display_errors();
}
}
else
{
$source = './uploads/' . $photo_id . '.jpg';
$destination = './uploads/photos/' . $photo_id . '.jpg';
rename($source, $destination);
/*//buggy php???
$result = copy($source, $destination);
echo "HO" . $result;
*/
}
//create thumbnail
$config['image_library'] = 'GD2';
$config['source_image'] = './uploads/photos/' . $photo_id . '.jpg';
$config['new_image'] = './uploads/thumbnails/' . $photo_id . '.jpg';
$config['create_thumb'] = TRUE;
$config['thumb_marker'] = '_thumb';
$config['maintain_ratio'] = TRUE;
$config['width'] = 180;//180
$config['height'] = 100;
$config['master_dim'] = 'width';
$this->image_lib->initialize($config);
$this->load->library('image_lib', $config);
$this->image_lib->resize();
redirect('photo/show/' . $photo_id);
}
//redirect('photo/photo_add/');
}
Well, I can answer part of your question - you can rotate images which carry the exif rotation using Codeigniter.
Firstly:
Detect the uploaded images exif data.
Look at the $exif array and handle the Orientation item.
Pass the required rotation degrees to codeigniters image_lib->rotate() function.
EG:
public function auto_rotate_image($upload_data){//iphone rotation fix
$path = $upload_data['full_path'];
$exif = exif_read_data($path);
if(isset($exif['Orientation'])){
$rotate = false;
switch($exif['Orientation']){//only really interested in the rotation
case 1: // nothing
break;
case 2:// horizontal flip
break;
case 3: // 180 rotate left
$rotate = 180;
break;
case 4: // vertical flip
break;
case 5: // vertical flip + 90 rotate right
break;
case 6: // 90 rotate right
$rotate = 270;
break;
case 7: // horizontal flip + 90 rotate right
break;
case 8: // 90 rotate left
$rotate = 90;
break;
}
if($rotate){
$config=array();
$config['image_library'] = 'gd2';
$config['source_image'] = $path;
$config['rotation_angle'] = $rotate;
$config['overwrite'] = TRUE;
$this->load->library('image_lib',$config);
if(!$this->image_lib->rotate()){
echo $this->image_lib->display_errors();
}
}
}
}
For all the work required to do your image manipulation using the objects that code igniter exposes for you, you may want to look into doing this yourself strictly with the gd functions in php. I have not used code igniter, but I've done a lot of stuff with image manipulation in php, and it's not terribly difficult.
Just by looking at your code, and I'm assuming this is the code igniter way, I see you calling image_lib->initialize() and load->library() for each individual manipulation. I'd have a look inside those methods and the rotate() and resize() methods you're using and see if they're creating and destroying the image resource with each manipulation. If you use the gd library, you can reuse the same image resource for each step and then just write it to file when you're ready (re-use the same image resource to create the thumbnail). This would probably make a huge performance gain in both execution speed and memory used.
GD Functions in PHP
You could store your configs elsewhere per codeigniter documentation, that would tighten the code up. I don't know how you could chain those events.