My publishing workflow is more or less automated. The only point is uploading images for linking and displaying. The dev.to
API doesn't support uploading images yet. To automate this part I decide to use Dropbox. Dropbox has an API which is more or less simple and is commonly used.
But before we can upload our images, we have to create our own Dropbox App
. With your own Dropbox App you have full control to upload your images, and get the image links for referencing in your blog entries.
Create Dropbox App for preparation
To create a Dropbox App go to Create App
After the App creation, we have to adjust the permissions. Go to the Dropbox App overview and click on your newly created App:
The App permissions need following rights:
We need also an access token for our script. In App settings
you can generate an access token without expiring date:
Save your access token for later use.
We finished the preparation on the Dropbox side.
Using the Dropbox API
Uploading images to Dropbox
We starting to discover the Dropbox API in the http section here
For now I am only interested in Content-upload. I'm following this example but adapting the path:
curl -X POST https://content.dropboxapi.com/2/files/upload \
--header "Authorization: Bearer <access Token> " \
--header "Dropbox-API-Arg: {\"path\": \"/Images/TestImage.png\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}" \
--header "Content-Type: application/octet-stream" \
--data-binary @"/home/user/path/to/image.png"
After the first test run you will find your uploads in Dropbox/Apps/[yourAppname]/Images
.
Now we need the sharing link
for this image. This is possible via:
Create sharing links
curl -X POST https://api.dropboxapi.com/2/sharing/create_sharing_link_with_settings \
--header "Authorization: Bearer <access Token>" \
--header "Content-Type: application/json" \
--data "{\"path\": \"/Prime_Numbers.txt\",\"settings\": {\"requested_visibility\": \"public\",\"audience\": \"public\",\"access\": \"viewer\"}}"
We can expect following return:
{
".tag": "file",
"url": "https://www.dropbox.com/s/2sn712vy1ovegw8/Prime_Numbers.txt?dl=0",
"name": "Prime_Numbers.txt",
"link_permissions": {
"can_revoke": false,
"resolved_visibility": {
".tag": "public"
},
"revoke_failure_reason": {
".tag": "owner_only"
}
},
"client_modified": "2015-05-12T15:50:38Z",
"server_modified": "2015-05-12T15:50:38Z",
"rev": "a1c10ce0dd78",
"size": 7212,
"id": "id:a4ayc_80_OEAAAAAAAAAXw",
"path_lower": "/homework/math/prime_numbers.txt",
"team_member_info": {
"team_info": {
"id": "dbtid:AAFdgehTzw7WlXhZJsbGCLePe8RvQGYDr-I",
"name": "Acme, Inc."
},
"display_name": "Roger Rabbit",
"member_id": "dbmid:abcd1234"
}
}
Fetch the sharing link
Theoretically we can use the output of the first response to get the sharing link. But if we want to use this script twice, you get a different response, with the message that the images is already exits. For This case we need a third API call just to fetch the sharing link.
The response from https://api.dropboxapi.com/2/sharing/list_shared_links
will be piped to jq with some sed
magic we replace the parameter dl=0
with raw=1
to use it in a markdown image link.
# Get the shared image url,
# get the url value from response with jq '.url'
# replaces link parameter dl=0 to raw=1
# removed " from string
url=$(curl -X POST https://api.dropboxapi.com/2/sharing/list_shared_links \
--header "Authorization: Bearer $dropboxApiToken" \
--header "Content-Type: application/json" \
--data "{\"path\": \"/Images/$imageName\"}" | jq '.links[0].url' | sed -e "s/dl=0/raw=1/g" -e "s/\"//g")
The last point of automation is to replace the links in the markdown files. This is also something what we can do with sed
.
After some try and errors, I come up with this little script.
#!/bin/bash
#grep all images in content and read as array
readarray -t content < <(grep "^\!\[.*\]" "$1")
for i in "${content[@]}"
do :
#Get image Name in between [ and ]
imageName=$(echo $i | sed -e"s/\!\[//g" -e "s/\]//g")
# Upload all images
curl -X POST https://content.dropboxapi.com/2/files/upload \
--header "Authorization: Bearer $dropboxApiToken" \
--header "Dropbox-API-Arg: {\"path\": \"/Images/$imageName\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}" \
--header "Content-Type: application/octet-stream" \
--data-binary @"/home/maren/development/blog/content/images/$imageName"
# Create sharing link
curl -X POST https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings \
--header "Authorization: Bearer $dropboxApiToken" \
--header "Content-Type: application/json" \
--data "{\"path\": \"/Images/$imageName\",\"settings\": {\"requested_visibility\": \"public\",\"audience\": \"public\",\"access\": \"viewer\"}}"
# Get the sharing image url,
# get the url value from response with jq '.url'
# replaces link parameter dl=0 to raw=1
# removed " from string
url=$(curl -X POST https://api.dropboxapi.com/2/sharing/list_shared_links \
--header "Authorization: Bearer $dropboxApiToken" \
--header "Content-Type: application/json" \
--data "{\"path\": \"/Images/$imageName\"}" | jq '.links[0].url' | sed -e "s/dl=0/raw=1/g" -e "s/\"//g")
# add url to image line
echo -e "=== REPLACE image tags $imageName in $1 ==="
sed -i "s#\[$imageName\]#\[$imageName\]\($url\)#g" "$1"
done
To use this script we have to follow some rules in our markdown.
We use the image tag as usual ![Image_name_without_spaces.png]
but we can left out the parentheses, the filename should not contain spaces and the image tag should be at the beginning of a line. This script will add the missing parentheses with the Dropbox image links.
In addition the images should be in a specific directory for convenience - in my case it is located in /home/maren/development/blog/content/images
The last step is to merge my old publishing script together. For better readability I split the script in multiple functions. You can find this script as gist here
Now the process is completely automated and flawless controlled via command line.
Top comments (2)
Another option is B2 storage from Backblaze. You get 10GB for free, and it's really easy to upload pictures. That's what I use for images for my static website.
I have a script that uses imagemagick to resize a picture into a few standard formats and then uploads all of them to B2. Maybe I should write an article about it... 😁
Why not, i just searched for a data hoster which is commonly used and provide an api. The idea is quite generic.
I think watermarking with imagemagick is something interesting as well.