{"id":3209,"date":"2017-03-10T14:37:57","date_gmt":"2017-03-10T14:37:57","guid":{"rendered":"http:\/\/www.nikola-breznjak.com\/blog\/?p=3209"},"modified":"2017-03-10T14:40:25","modified_gmt":"2017-03-10T14:40:25","slug":"self-hosted-image-upload-resize-script-go","status":"publish","type":"post","link":"https:\/\/nikola-breznjak.com\/blog\/miscellaneou\/self-hosted-image-upload-resize-script-go\/","title":{"rendered":"Self-Hosted Image Upload and Resize Script in Go"},"content":{"rendered":"<p>Recently I researched solutions for image upload and resizing, and I&#8217;ve looked at a few open source solutions and settled for this one: <a href=\"https:\/\/github.com\/thoas\/picfit\">picfit<\/a>.<\/p>\n<p>It&#8217;s written in Go, it&#8217;s simple to set up and use, and it has S3 support out of the box.<\/p>\n<p>The official introduction tutorial is <a href=\"https:\/\/medium.com\/@thoas\/introduction-to-picfit-an-image-resizing-server-written-in-go-c8320c017d41#.7heq5cq91\">here<\/a>. However, I&#8217;m going to outline my steps here as well since I installed it on my Digital Ocean droplet differently than it&#8217;s written in the tutorial.<\/p>\n<h2>Installation<\/h2>\n<blockquote><p>\n  I&#8217;m using DigitalOcean for all my testing. If you signup using <a href=\"https:\/\/m.do.co\/c\/974c9bc93d77\">this link<\/a> you&#8217;ll get 10$ which will be enough to run a test &#8216;droplet&#8217; for two months.\n<\/p><\/blockquote>\n<p>My Droplet runs on Ubuntu,\u00a0so I installed it by following the official <a href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-install-go-1-6-on-ubuntu-14-04\">Digital Ocean tutorial<\/a>:<\/p>\n<h3>Install Go<\/h3>\n<pre><code>sudo apt-get update\nsudo apt-get -y upgrade\nsudo curl -O https:\/\/storage.googleapis.com\/golang\/go1.8.linux-amd64.tar.gz\nsudo tar -xvf go1.8.linux-amd64.tar.gz\nsudo mv go \/usr\/local\n<\/code><\/pre>\n<h3>Add Go to the PATH variable<\/h3>\n<p>Aappend this line to the <code>~\/.profile<\/code> file:<\/p>\n<p><code>export PATH=$PATH:\/usr\/local\/go\/bin<\/code><\/p>\n<h3>Enable terminal to pull new settings<\/h3>\n<p><code>source ~\/.profile<\/code><\/p>\n<h3>Download picfit<\/h3>\n<ul>\n<li>Create new folder for Go programs: <code>mkdir $HOME\/work<\/code><\/li>\n<li>Set the path to it in the GOPATH variable: <code>export GOPATH=$HOME\/work<\/code><\/li>\n<li>Get picfit via go: <code>go get github.com\/thoas\/picfit<\/code><\/li>\n<li>Make a build:<\/li>\n<\/ul>\n<pre><code>cd ~\/work\/src\/github.com\/thoas\/picfit\nmake build\ncd bin\n<\/code><\/pre>\n<h3>Set the configuration<\/h3>\n<p>Create the <code>config.json<\/code> file inside the <code>bin<\/code> folder:<\/p>\n<pre><code>{\n  \"port\": 3117,\n  \"storage\": {\n    \"src\": {\n      \"type\": \"fs\",\n      \"location\": \"\/home\/nikola\/work\/src\/github.com\/thoas\/picfit\/bin\"\n    }\n  },\n  \"kvstore\": {\n    \"type\": \"cache\"\n  }\n}\n<\/code><\/pre>\n<h3>Run it<\/h3>\n<p>Finally, run picfit with: <code>.\/picfit -c config.json<\/code><\/p>\n<p>If everything goes fine you should see an output like this:<\/p>\n<pre><code>[GIN-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:    export GIN_MODE=release\n - using code:    gin.SetMode(gin.ReleaseMode)\n\n[GIN-debug] GET    \/stats                    --&gt; github.com\/thoas\/picfit\/server.Router.func2 (10 handlers)\n[GIN-debug] GET    \/redirect                 --&gt; github.com\/thoas\/picfit\/views.RedirectView (16 handlers)\n[GIN-debug] GET    \/redirect\/*parameters     --&gt; github.com\/thoas\/picfit\/views.RedirectView (16 handlers)\n[GIN-debug] GET    \/display                  --&gt; github.com\/thoas\/picfit\/views.DisplayView (16 handlers)\n[GIN-debug] GET    \/display\/*parameters      --&gt; github.com\/thoas\/picfit\/views.DisplayView (16 handlers)\n[GIN-debug] GET    \/get                      --&gt; github.com\/thoas\/picfit\/views.GetView (16 handlers)\n[GIN-debug] GET    \/get\/*parameters          --&gt; github.com\/thoas\/picfit\/views.GetView (16 handlers)\n[GIN-debug] Listening and serving HTTP on :3117\n<\/code><\/pre>\n<h2>Usage<\/h2>\n<p>Copied from the official tutorial:<\/p>\n<p><code>http:\/\/localhost:3117\/{method}?url={url}&amp;path={path}&amp;w={width}&amp;h={height}&amp;upscale={upscale}&amp;sig={sig}&amp;op={operation}&amp;fmt={format}<\/code><\/p>\n<ul>\n<li><code>path<\/code> &#8211; file path to load the image using your source storage (optional, if you haven\u2019t configured a source storage)<\/li>\n<li><code>method<\/code> &#8211; operation to perform: <code>display<\/code>, <code>redirect<\/code> or <code>get<\/code><\/li>\n<li><code>sig<\/code> &#8211; signature key which is the representation of your query string and your secret key (optional, if you haven\u2019t configured a secret key)<\/li>\n<li><code>url<\/code> &#8211; url of the image which will be retrieved by HTTP (optional, if path is provided)<\/li>\n<li><code>width<\/code> &#8211; desired width of the image, if 0 is provided the service will calculate the ratio with height<\/li>\n<li><code>height<\/code> &#8211; desired height of the image, if 0 is provided the service will calculate the ratio with width<br \/>\nupscale\u200a\u2014\u200aIf your image is smaller than your desired dimensions, the service will upscale it by default to fit your dimensions, you can disable this behavior by providing 0 (optional)<\/li>\n<li><code>format<\/code> &#8211; output format to save the image, by default the format will be the source format, for example, a GIF image source will be saved as GIF (optional)<\/li>\n<\/ul>\n<h2>Examples<\/h2>\n<p>The links are for your localhost (change to your IP if you&#8217;ll be testing this somewhere else):<\/p>\n<ul>\n<li><a href=\"http:\/\/localhost:3117\/display?url=http:\/\/www.google.com\/images\/srpr\/logo11w.png&amp;w=200&amp;h=0&amp;op=resize\">Create smaller image<\/a><\/li>\n<li><a href=\"http:\/\/localhost:3117\/display?url=http:\/\/www.google.com\/images\/srpr\/logo11w.png&amp;w=2000&amp;op=resize\">Upscale<\/a><\/li>\n<li><a href=\"http:\/\/localhost:3117\/display?url=http:\/\/www.google.com\/images\/srpr\/logo11w.png&amp;w=2000&amp;op=resize&amp;upscale=0\">Don&#8217;t upscale<\/a><\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>Sure, this solution doesn&#8217;t have all the bells and whistles that you may want. But, the awesome thing is that it can extend to your liking.<\/p>\n<p>For example, if you want to add some watermark to a particular image, you could do that (of course, given that you know Go. If not, there are some other solutions for other languages like for example <a href=\"https:\/\/github.com\/verot\/class.upload.php\/blob\/master\/README.md\">this one<\/a> for PHP.<\/p>\n<p>If you don&#8217;t like messing with your solution, I would recommend you check out Cloudinary (cloudinary.com). It&#8217;s a paid solution, but it even has a free tier which you can use for testing, and it has some really <a href=\"http:\/\/cloudinary.com\/features#manipulation\">advanced image manipulation features<\/a>.<\/p>\n<p>Do you maybe have some other to recommend?<\/p>\n<blockquote class=\"twitter-tweet\" data-width=\"550\">\n<p lang=\"en\" dir=\"ltr\">Self-Hosted <a href=\"https:\/\/twitter.com\/hashtag\/Image?src=hash\">#Image<\/a> Upload and <a href=\"https:\/\/twitter.com\/hashtag\/Resize?src=hash\">#Resize<\/a> Script in <a href=\"https:\/\/twitter.com\/hashtag\/Go?src=hash\">#Go<\/a> &#8211; <a href=\"https:\/\/t.co\/Wxh5FMMhHk\">https:\/\/t.co\/Wxh5FMMhHk<\/a><\/p>\n<p>&mdash; Nikola Bre\u017enjak (@HitmanHR) <a href=\"https:\/\/twitter.com\/HitmanHR\/status\/840210535924789248\">March 10, 2017<\/a><\/p><\/blockquote>\n<p><script async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I researched solutions for image upload and resizing, and I&#8217;ve looked at a few open source solutions and settled for this one: picfit. It&#8217;s written in Go,&hellip;<\/p>\n","protected":false},"author":1,"featured_media":3210,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[32],"tags":[],"class_list":["post-3209","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-miscellaneou"],"_links":{"self":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts\/3209","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/comments?post=3209"}],"version-history":[{"count":3,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts\/3209\/revisions"}],"predecessor-version":[{"id":3213,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts\/3209\/revisions\/3213"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/media\/3210"}],"wp:attachment":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/media?parent=3209"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/categories?post=3209"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/tags?post=3209"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}