Attachment Improvements By Xon

Attachment Improvements By Xon 2.5.0

No permission to download
@Xon not sure if I'm missing something obvious here?

Trying to use this with the S3 addon again:

// S3 File Storage
$s3 = function()
   return new \Aws\S3\S3Client([
      'credentials' => [
         'key' => 'XX',
         'secret' => 'XX'
      'region' => 'eu-central-1',
      'version' => 'latest',
      'endpoint' => ''

$config['fsAdapters']['data'] = function() use($s3)
   return new \League\Flysystem\AwsS3v3\AwsS3Adapter($s3(), 'XX', 'data');

$config['externalDataUrl'] = function($externalPath, $canonical)
   return '' . $externalPath;

$config['fsAdapters']['internal-data'] = function() use($s3)
   return new \League\Flysystem\AwsS3v3\AwsS3Adapter($s3(), 'XX', 'internal_data');

$config['internalDataUrl'] = function($externalPath, $canonical)
    return 'internal_data_s3/..../internal_data/' . $externalPath;

location ~* /internal_data_s3/(.*?)://(.*?)/(.*) {
    set $xfEtag $upstream_http_etag;
    set $download_protocol $1;
    set $download_host $2;
    set $download_path $3;
    set $download_url $download_protocol://$download_host/$download_path;

    resolver ipv6=off;
    proxy_set_header Host $download_host;
    proxy_set_header Authorization '';
    proxy_set_header Cookie '';
    proxy_max_temp_file_size 0;
    proxy_intercept_errors on;
    error_page 301 302 307 = @handle_redirect;

    proxy_pass $download_url$is_args$args;

    proxy_hide_header Content-Disposition;
    proxy_hide_header Content-Type;
    proxy_hide_header Etag;
    proxy_hide_header x-amz-request-id;

    add_header Etag $xfEtag;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;

location @handle_redirect {
   resolver ipv6=off;
   set $saved_redirect_location '$upstream_http_location';
   proxy_pass $saved_redirect_location;

Gives the below error when trying to view the full image
2021/03/31 12:48:39 [error] 7749#7749: *1 rewrite or internal redirection cycle while internally redirecting to "/index.php", client:, server:, request: "GET /attachments/1616058413600-png.9348/ HTTP/2.0", upstream: "fastcgi://", host: "", referrer: ""
^^ Actually, I think I have this working now.

$config['internalDataUrl'] = function($externalPath, $canonical)
    return 'internal_data/' . $externalPath;
Thanks @Xon.

One more thing if you don't mind? I've put the S3 bucket behind Cloudflare, so changed the URL, however, it breaks again with the Nginx X-Accel-Redirect option enabled:

2021/03/31 17:06:20 [error] 42245#42245: *16164 SSL_do_handshake() failed (SSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:SSL alert number 40) while SSL handshaking to upstream, client:, server:, request: "GET /attachments/20170503_152845-jpg.9399/ HTTP/2.0", upstream: "", host: "", referrer: ""

It's replacing the FQDN of the CNAME for the bucket with the IP of Cloudflare, causing a 502 error.
Last edited:
I'm frankly not sure how to make that work, as it is something of a browser compatibility issue.

I'll look into re-using the Svg Template functionality for rendering SVGs to PNGs to see if that can help.
Any plans to be able to rename attachments if one can edit a piece of content?

This would be good for SEO (and perhaps be more accessible to the blind) in order to rename files something descriptive.
What could be the cause for this error?

TypeError: Return value of SV\AttachmentImprovements\SvgFileWrapper::getImageType() must be of the type string, null returned src/addons/SV/AttachmentImprovements/SvgFileWrapper.php:19
Generated by: admin Jun 22, 2021 at 9:41 PM

Stack trace

#0 src/addons/SV/AttachmentImprovements/XF/Service/Attachment/Preparer.php(96): SV\AttachmentImprovements\SvgFileWrapper->getImageType()
#1 src/XF/Job/AttachmentThumb.php(54): SV\AttachmentImprovements\XF\Service\Attachment\Preparer->generateAttachmentThumbnail('/home/nginx/dom...', 479, 250)
#2 src/XF/Job/Manager.php(258): XF\Job\AttachmentThumb->run(8)
#3 src/XF/Job/Manager.php(200): XF\Job\Manager->runJobInternal(Array, 8)
#4 src/XF/Job/Manager.php(84): XF\Job\Manager->runJobEntry(Array, 8)
#5 src/XF/Admin/Controller/Tools.php(139): XF\Job\Manager->runQueue(true, 8)
#6 src/XF/Mvc/Dispatcher.php(350): XF\Admin\Controller\Tools->actionRunJob(Object(XF\Mvc\ParameterBag))
#7 src/XF/Mvc/Dispatcher.php(257): XF\Mvc\Dispatcher->dispatchClass('XF:Tools', 'RunJob', Object(XF\Mvc\RouteMatch), Object(Truonglv\ImageOptimizer\XF\Admin\Controller\Tools), NULL)
#8 src/XF/Mvc/Dispatcher.php(113): XF\Mvc\Dispatcher->dispatchFromMatch(Object(XF\Mvc\RouteMatch), Object(Truonglv\ImageOptimizer\XF\Admin\Controller\Tools), NULL)
#9 src/XF/Mvc/Dispatcher.php(55): XF\Mvc\Dispatcher->dispatchLoop(Object(XF\Mvc\RouteMatch))
#10 src/XF/App.php(2337): XF\Mvc\Dispatcher->run()
#11 src/XF.php(488): XF\App->run()
#12 admin.php(13): XF::runApp('XF\\Admin\\App')
#13 {main}
Top Bottom