Skip to content

Commit 32f6f6e

Browse files
committed
Support ability to create thumbnails on image uploads
1 parent 85871f2 commit 32f6f6e

File tree

2 files changed

+201
-75
lines changed

2 files changed

+201
-75
lines changed

disciple-tools-storage-api.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,59 @@ public static function generate_random_string( $length = 112 ): string {
5050
return $random_string;
5151
}
5252

53+
public static function generate_image_thumbnail( $src, $content_type, $desired_width ) {
54+
$thumbnail = null;
55+
try {
56+
57+
// Read the original source image, by respective content type.
58+
switch ( strtolower( trim( $content_type ) ) ) {
59+
case 'image/gif':
60+
$source_image = imagecreatefromgif( $src );
61+
break;
62+
case 'image/jpeg':
63+
$source_image = imagecreatefromjpeg( $src );
64+
break;
65+
case 'image/png':
66+
$source_image = imagecreatefrompng( $src );
67+
break;
68+
default:
69+
$source_image = null;
70+
break;
71+
}
72+
73+
if ( !empty( $source_image ) ) {
74+
75+
// Determine sourced image dimensions.
76+
$width = imagesx( $source_image );
77+
$height = imagesy( $source_image );
78+
79+
// Find the "desired height" of this thumbnail, relative to the desired width.
80+
$desired_height = floor( $height * ( $desired_width / $width ) );
81+
82+
// Create a new, "virtual" image.
83+
$virtual_image = imagecreatetruecolor( $desired_width, $desired_height );
84+
85+
// Support background transparency.
86+
$black = imagecolorallocate( $virtual_image, 0, 0, 0 );
87+
imagecolortransparent( $virtual_image, $black );
88+
89+
// Copy source image at a resized size.
90+
imagecopyresampled( $virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height );
91+
92+
// Ensure there is a valid virtual image to be processed.
93+
if ( !empty( $virtual_image ) ) {
94+
95+
// Next, capture virtual image to be returned.
96+
$thumbnail = $virtual_image;
97+
}
98+
}
99+
} catch ( Exception $e ) {
100+
$thumbnail = null;
101+
}
102+
103+
return $thumbnail;
104+
}
105+
53106
public static function validate_url( $url ): string {
54107
if ( !filter_var( $url, FILTER_VALIDATE_URL ) ) {
55108
$http = 'http://';

disciple-tools-storage-filters.php

Lines changed: 148 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,22 @@ public static function get_file_url( string $key ): string {
4343
* @param string $existing_key
4444
* @return false|mixed
4545
*/
46-
public static function upload_file( string $key_prefix = '', array $upload = [], string $existing_key = '' ){
46+
public static function upload_file( string $key_prefix = '', array $upload = [], string $existing_key = '', array $args = [] ){
4747
$key_prefix = trailingslashit( $key_prefix );
4848
$connection = self::get_connection();
49-
$args = [
49+
$merged_args = array_merge( $args, [
5050
'auto_generate_key' => empty( $existing_key ),
5151
'include_extension' => empty( $existing_key ),
52-
'default_key' => $existing_key
53-
];
52+
'default_key' => $existing_key,
53+
'auto_generate_thumbnails' => in_array( strtolower( trim( $upload['type'] ?? '' ) ), [
54+
'image/gif',
55+
'image/jpeg',
56+
'image/png'
57+
] ),
58+
'thumbnails_desired_width' => 32 // Heights are automatically calculated, based on specified width.
59+
] );
5460
if ( !empty( $connection ) ) {
55-
return dt_storage_connections_obj_upload( null, $connection->id, $key_prefix, $upload, $args );
61+
return dt_storage_connections_obj_upload( null, $connection->id, $key_prefix, $upload, $merged_args );
5662
}
5763
return false;
5864
}
@@ -184,7 +190,6 @@ function dt_storage_connections_obj_upload( $response, $storage_connection_id, $
184190

185191
$s3 = null;
186192
$response = true;
187-
$upload_id = null;
188193
$bucket = $config['bucket'];
189194

190195
// Generate complete file key name to be used moving forward.
@@ -206,84 +211,152 @@ function dt_storage_connections_obj_upload( $response, $storage_connection_id, $
206211
'endpoint' => $endpoint
207212
] );
208213

214+
// First, upload original file.
215+
$uploaded_key = dt_storage_connections_obj_upload_s3( $s3, $bucket, $key_name, $upload );
216+
217+
// Next, if specified, generate and upload a corresponding thumbnail.
218+
$uploaded_thumbnail_key = null;
219+
if ( isset( $args['auto_generate_thumbnails'] ) && $args['auto_generate_thumbnails'] ) {
220+
$thumbnail = Disciple_Tools_Storage_API::generate_image_thumbnail( $upload['tmp_name'], $upload['type'] ?? '', $args['thumbnails_desired_width'] ?? 32 );
221+
if ( !empty( $thumbnail ) ) {
222+
223+
// Generate temp file to function as a reference point for generated thumbnail.
224+
$tmp_image = tmpfile();
225+
$tmp_image_metadata = stream_get_meta_data( $tmp_image );
226+
227+
// Next, populate temp file, accordingly, by image content type.
228+
$thumbnail_tmp_name = null;
229+
switch ( strtolower( trim( $upload['type'] ?? '' ) ) ) {
230+
case 'image/gif':
231+
if ( imagegif( $thumbnail, $tmp_image ) ) {
232+
$thumbnail_tmp_name = $tmp_image_metadata['uri'];
233+
}
234+
break;
235+
case 'image/jpeg':
236+
if ( imagejpeg( $thumbnail, $tmp_image ) ) {
237+
$thumbnail_tmp_name = $tmp_image_metadata['uri'];
238+
}
239+
break;
240+
case 'image/png':
241+
if ( imagepng( $thumbnail, $tmp_image ) ) {
242+
$thumbnail_tmp_name = $tmp_image_metadata['uri'];
243+
}
244+
break;
245+
default:
246+
break;
247+
}
248+
249+
// If we have a valid thumbnail temp file, proceed with upload attempt.
250+
if ( !empty( $thumbnail_tmp_name ) ) {
251+
252+
// Adjust reference to temp file, for recently generated thumbnail.
253+
$upload['tmp_name'] = $thumbnail_tmp_name;
254+
255+
// Fetch thumbnail sizing info, to be used to generate suffix for key_name.
256+
$thumbnail_size = getimagesize( $thumbnail_tmp_name );
257+
if ( !empty( $thumbnail_size ) ) {
258+
$thumbnail_key_name = $key_name . '.' . $thumbnail_size[0] . 'x' . $thumbnail_size[1];
259+
$uploaded_thumbnail_key = dt_storage_connections_obj_upload_s3( $s3, $bucket, $thumbnail_key_name, $upload );
260+
}
261+
}
262+
}
263+
}
209264

210-
// Upload file in parts, to better manage memory leaks.
211-
$result = $s3->createMultipartUpload( [
212-
'Bucket' => $bucket,
213-
'Key' => $key_name,
214-
// 'StorageClass' => 'REDUCED_REDUNDANCY', // NB: Currently not supported by Backblaze
215-
// 'ACL' => 'public-read', // NB: Currently not supported by Backblaze
216-
'ContentType' => $upload['type'] ?? '',
217-
'Metadata' => []
218-
] );
219-
$upload_id = $result['UploadId'];
220-
265+
// Finally, capture valid uploaded keys.
266+
$response = [
267+
'uploaded_key' => ! empty( $uploaded_key ) ? $uploaded_key : null,
268+
'uploaded_thumbnail_key' => ! empty( $uploaded_thumbnail_key ) ? $uploaded_thumbnail_key : null
269+
];
221270
} catch ( Exception $e ) {
222271
$response = false;
223272
}
273+
}
274+
break;
275+
default:
276+
break;
277+
}
278+
}
224279

225-
// Ensure no previous upload exceptions have been encountered.
226-
if ( $response !== false ) {
227-
$parts = [];
228-
229-
try {
230-
231-
// Start to upload file in partial chunks.
232-
$filename = $upload['tmp_name'] ?? '';
233-
$file = fopen( $filename, 'r' );
234-
$part_number = 1;
235-
while ( !feof( $file ) ) {
236-
$result = $s3->uploadPart( [
237-
'Bucket' => $bucket,
238-
'Key' => $key_name,
239-
'UploadId' => $upload_id,
240-
'PartNumber' => $part_number,
241-
'Body' => fread( $file, 5 * 1024 * 1024 ),
242-
] );
243-
244-
$parts['Parts'][$part_number] = [
245-
'PartNumber' => $part_number,
246-
'ETag' => $result['ETag'],
247-
];
248-
249-
// Increment part count and force garbage collection, to better manage memory.
250-
$part_number++;
251-
gc_collect_cycles();
252-
}
253-
fclose( $file );
254-
255-
} catch ( Exception $e ) {
256-
$response = false;
257-
$result = $s3->abortMultipartUpload( [
258-
'Bucket' => $bucket,
259-
'Key' => $key_name,
260-
'UploadId' => $upload_id
261-
] );
262-
}
280+
return $response;
281+
}
263282

264-
// Ensure no previous upload exceptions have been encountered.
265-
if ( $response !== false ) {
283+
function dt_storage_connections_obj_upload_s3( $s3, $bucket, $key_name, $upload ) {
284+
$response = null;
285+
$upload_id = null;
286+
287+
try {
288+
289+
// Upload file in parts, to better manage memory leaks.
290+
$result = $s3->createMultipartUpload( [
291+
'Bucket' => $bucket,
292+
'Key' => $key_name,
293+
// 'StorageClass' => 'REDUCED_REDUNDANCY', // NB: Currently not supported by Backblaze
294+
// 'ACL' => 'public-read', // NB: Currently not supported by Backblaze
295+
'ContentType' => $upload['type'] ?? '',
296+
'Metadata' => []
297+
] );
298+
$upload_id = $result['UploadId'];
299+
300+
} catch ( Exception $e ) {
301+
$response = false;
302+
}
266303

267-
try {
304+
// Ensure no previous upload exceptions have been encountered.
305+
if ( $response !== false ) {
306+
$parts = [];
307+
308+
try {
309+
310+
// Start to upload file in partial chunks.
311+
$filename = $upload['tmp_name'] ?? '';
312+
$file = fopen( $filename, 'r' );
313+
$part_number = 1;
314+
while ( !feof( $file ) ) {
315+
$result = $s3->uploadPart( [
316+
'Bucket' => $bucket,
317+
'Key' => $key_name,
318+
'UploadId' => $upload_id,
319+
'PartNumber' => $part_number,
320+
'Body' => fread( $file, 5 * 1024 * 1024 ),
321+
] );
322+
323+
$parts['Parts'][$part_number] = [
324+
'PartNumber' => $part_number,
325+
'ETag' => $result['ETag'],
326+
];
327+
328+
// Increment part count and force garbage collection, to better manage memory.
329+
$part_number++;
330+
gc_collect_cycles();
331+
}
332+
fclose( $file );
333+
334+
} catch ( Exception $e ) {
335+
$response = false;
336+
$result = $s3->abortMultipartUpload( [
337+
'Bucket' => $bucket,
338+
'Key' => $key_name,
339+
'UploadId' => $upload_id
340+
] );
341+
}
268342

269-
// Complete the multipart upload.
270-
$result = $s3->completeMultipartUpload( [
271-
'Bucket' => $bucket,
272-
'Key' => $key_name,
273-
'UploadId' => $upload_id,
274-
'MultipartUpload' => $parts,
275-
] );
276-
$response = $result['Key'] ?? false;
343+
// Ensure no previous upload exceptions have been encountered.
344+
if ( $response !== false ) {
277345

278-
} catch ( Exception $e ) {
279-
$response = false;
280-
}
281-
}
282-
}
283-
}
284-
break;
285-
default:
286-
break;
346+
try {
347+
348+
// Complete the multipart upload.
349+
$result = $s3->completeMultipartUpload( [
350+
'Bucket' => $bucket,
351+
'Key' => $key_name,
352+
'UploadId' => $upload_id,
353+
'MultipartUpload' => $parts,
354+
] );
355+
$response = $result['Key'] ?? false;
356+
357+
} catch ( Exception $e ) {
358+
$response = false;
359+
}
287360
}
288361
}
289362

0 commit comments

Comments
 (0)