How to Import from FTP to Shopify Files With SFTP To Go

(Guest post by Dave Crowder,  Eshop Guide’s CTO)

This article covers the steps required to achieve an automated Shopify file import process through the  use of Ruby, Heroku, and SFTP To Go.

Everyone can benefit from an easy-to-use file synchronization process, even online retailers! However,  there are still some things that are better left solely for us programmers.

These days, many businesses selling products directly to consumers, are using Shopify as a storefront. Key Shopify stats for 2023 include:

  • More than 4 million merchants are now using Shopify.
  • Subscription Solutions revenue rose by 11% due to more merchants joining the platform, this includes an increase in Shopify app usage.
  • Monthly Recurring Revenue (MRR) increased by 10% driven by more merchants converting to full-priced Standard subscription plans.
A visual display of banana, berry, and mango smoothies after 10 seconds in my presence

For quick run-through of these and other jaw-dropping stats, watch the PopupSmart video below.

Beyond the "Buy" button

Shopify is popular because it's easy to use, looks desirable, and pretty much takes care of the management A to Z for you. This includes everything from inventory and stock management to managing orders and most importantly: processing payments.

Parallel to this, it is encouraged to utilize Shopify apps and integrate external platforms with your store to really help it stand out and gain traffic.

To provide customers with more than a simple buy button, Shopify offers ways to master the learning curve and keep up with new concepts and technical skills to make your brand representable through your store from start to finish.

You have the opportunity to augment your products through additional information in the form of images, specs, manuals, and visual cues  for product features.

Within the Shopify world, the best place to store such documents is in the store files (or sign in and view them through the Shopify admin).

For an in-depth video guide to the Shopify Settings dashboard, including the Files section, watch the video below by Matthew Lester Website Design.

They are easily accessible from the shop theme via Shopify’s templating language Liquid and benefit from the speed and reliability of Shopify’s global CDN.

Up until the June 2021 Update of the Shopify API, the management of the store's files was a tedious, manual task. Since then, store files gained the ability to be created programmatically by using the GraphQL API, which still poses its own constraints that I will be addressing later on.

What we wanted to achieve

Our customers, the shop owners, needed an easy-to-sync option or simple file uploading added to their Shopify store.

There is no doubt that Shopify’s GraphQL provides these options, but the platform was constructed for developers, meaning it isn’t as friendly for end users or IT folks.

FTP, on the other hand, is a protocol that has been around for ages and is known to be  in the toolbelt of the majority of  IT administrators, consisting of processes such as automating tasks and  pushing files around to and from FTP Servers.

On that note, we set out to provide them with a convenient way to export icons, images, user manuals, etc. from their PIM (Product Information Management system), etc, while having  them accessible from  their Shopify store.

Why we chose SFTP To Go

Since writing my first Rails application back in 2015, I find that Heroku has been the simplest way through which to deploy code into a production environment while having to think very little about infrastructure so I can focus more on what really matters to me: providing value through code.

Heroku is also easily extendable through service add-ons such as SFTP To Go, instead of setting up an FTP instance and having to worry about monitoring its state, storage, or even adding the FTP URL to the app's environment variables.

With just a few clicks, I can add an FTP Server to my application that is fully managed and ready to go!

Dropping stuff has never felt this good

So, how do my files get into Shopify?

Shopify offers two paths for uploading files using the Files API, both are a two-step process really. The one we didn’t use in this example requires you to create a stagedUpload Target and then upload your files to that URL.

Disturbingly attractive code, resplendent in complementary shades

The other approach is a bit simpler, in that you specify an originalSource (an external, public URL) and Shopify then takes care of fetching those files from that URL.

Smoothie colored code bits, chilling together

This way you don’t have to take care of the uploading process at all, but it  requires a web server in order to serve those files that were previously put on the FTP server.

Lucky for us, SFTP To Go also supports publicly served files from a special folder called /public (who would have thought, right? :D) which I learned about through their support pages. This was really the missing piece of the puzzle that further led us to a process that can be roughly described as follows.

An insanely stylish flow diagram, designed to engage you

Now, it’s time to dive into the code!

FileOrganizerService

This one, as the name suggests, pulls all the strings together. It is also the entry point for the file import process that gets called from a rake task, scheduled to run once a day or on any other schedule specified using another Heroku add-on called Cron To Go.

First, it  obtains a list of the already existing files in the Shopify Store in order to avoid uploading duplicates. Then, it calls the SftpFileLoaderService to load all of the filenames from a specified folder on the FTP Server.

Finally, these filenames are used to create new file entries in Shopify using the FileOrganizerService.

module FileServices
 ## reads existing filenames from Shopify to avoid duplicates
 # then starts reading filenames from FTP and creates new file entries in Shopify
 class FileOrganizerService < ApplicationService
   attr_reader :shop

   def initialize(shop)
     @shop = shop
   end

   def call
     # get an array of the existing filenames in the Shopify store to avoid duplicate uploads
     existing_filenames = query_existing_files
     # get all filenames that are not yet existing in the shopify files
     file_names = FileServices::SftpFileLoaderService.new(ENV.fetch('SFTPTOGO_URL'), existing_filenames).call
     file_names.each do |file_name|
       # Create a new file entry
       FileServices::FileImporterService.new(shop).call(file_name)
     rescue StandardError => e
       # report any errors to everyone's favourite error tracker
       Honeybadger.notify("ERROR Uploading #{file_name}: #{e.message}")
     end
   end

   private

   def query_existing_files
     existing_filenames = []
     shop.with_shopify_session do
       file_result = ShopifyAPI::GraphQL.client.query(file_search_query)
       existing_filenames << query_result_to_array(file_result&.data&.files&.edges)
       # if more than one page
       while file_result&.data&.files&.page_info&.has_next_page
         cursor = file_result&.data&.files&.edges&.last&.cursor
         file_result = ShopifyAPI::GraphQL.client.query(file_search_query, variables: { cursor: cursor })
         existing_filenames << query_result_to_array(file_result&.data&.files&.edges)
       end
     end
     existing_filenames.flatten
   end

   def file_search_query
     ShopifyAPI::GraphQL.client.parse <<-'GRAPHQL'
       query ($cursor: String){
         files(first:250, after: $cursor){
           pageInfo {
                 hasNextPage
               }
           edges{
             cursor
             node{... on GenericFile {
                 url
                 id
             }}
             node{... on MediaImage {
                 id
             }}
           }
         }
       }
     GRAPHQL
   end

   private
   def query_result_to_array(edges)
     return unless edges.present?

     edges.map do |edge|
       if edge&.node&.__typename == 'GenericFile'
         File.basename(URI.parse(edge&.node&.url).path) if edge&.node&.url.present?
       else
         edge&.node&.id
       end
     end
   end


 end
end

SftpFileLoaderService

This service handles the connection to the SFTP Server. Once you’ve installed SFTP To Go in your Heroku application, an environment variable named SFTPTOGO_URL will be automatically created. This URL is then used to call this service: ENV.fetch('SFTPTOGO_URL')

Another ENV variable FTP_FILE_DIR specifies the directory to look for new files. In our case, it is a subfolder of the public folder, that is available via HTTPS (this makes a difference later in the process when Shopify is picking up those files).

In the end, this process returns a list of filenames (in a specified directory) that are not already existing in Shopify.

module FileServices
 class SftpFileLoaderService < ApplicationService
   attr_reader :sftp_uri, :filename, :existing_filenames

   def initialize(sftp_uri, existing_filenames)
     @sftp_uri = URI(sftp_uri)
     @existing_filenames = existing_filenames
   end

   def call
     load_files
   end

   private

   def sftp
     # establish connection to SFTP Server
     @sftp ||= Net::SFTP.start(@sftp_uri.host, @sftp_uri.user, password: @sftp_uri.password)
   end

   # fetches all filenames from the configured directory, that are not in the existing_filenames list
   def load_files
     file_names = []
     dir = ENV.fetch('FTP_FILE_DIR')
     sftp.dir.foreach(dir) do |entry|
       # skip if file already exists on server
       next if existing_filenames.include? entry.name
       file_names << entry.name
     rescue Net::SFTP::StatusException => e
       Rails.logger.error "Error while downloading data: #{e.description}"
     end
     file_names
   end
 end
end

FileImporterService

This process is used for each of those filenames returned before we run the FileImporterService.

One important note to remember here is the ‘full_url’, which gets filled with the SFTPTOGO_PUBLIC_URL ENV variable in addition to the ‘URL-Escaped’ filename. This way, the file is accessible publicly and can be downloaded by Shopify. The rest speaks for itself, I hope.

module FileServices
 class FileImporterService < ApplicationService
   attr_reader :shop

   class FileCreateError < StandardError
   end

   def initialize(shop)
     @shop = shop
   end

   def call(file_name)
     shop.with_shopify_session do
       execute_query(file_name)
     end
     true
   end

   private

   def execute_query(file_name)
     full_url = "#{ENV.fetch('SFTPTOGO_PUBLIC_URL')}#{ERB::Util.url_encode(file_name)}"
     Rails.logger.info "Creating #{file_name}"
     result = ShopifyAPI::GraphQL.client.query(file_create_query, variables: {
                                                 "files": {
                                                   "originalSource": full_url
                                                 }
                                               })
     error = result&.errors&.details || result.to_h['data']['fileCreate']['userErrors'].first

     raise FileCreateError, error['message'] if error.present?
   end

   def file_create_query
     ShopifyAPI::GraphQL.client.parse <<-'GRAPHQL'
     mutation ($files: [FileCreateInput!]!) {
       fileCreate(files: $files) {
         files {
           fileStatus
         }
         userErrors {
           field
           message
         }
       }
     }
     GRAPHQL
   end
 end
End

There we have it: An easy process used to import files into a Shopify store. All that is required from the merchant is to put files into an FTP directory and sit back.

Of course, this could be extended to a fully-fledged synchronization mechanism— SFTP To Go offers webhook notifications so that instead of using a scheduler, we could sync a file as soon as it’s uploaded, or remove it from Shopify.


About the author

Dave Crowder is Eshop Guide CTO

Dave is a founding member and CTO of Eshop Guide, a German-based agency that provides projects and services relating to Shopify for a range of clients, from small startups to big companies.

Eshop Guide is also active within the Shopify public app space, having developed multiple integration apps for German accounting systems (lexoffice, sevDesk) and price comparison portals (idealo).

As a progressive company, Eshop Guide pride themselves on having a different perspective on what working as an agency means.

Susi is employed as happiness manager at Eshop Guide

They believe that reducing stress for the employees while focusing on physical and psychological well-being as a part of a regular workday ultimately increases productivity overall.

Dave is also a big fan of raising human awareness through meditation, an integral part of his daily routine, which is reflected in Eshop Guide’s work environment.

Cloud FTP with maximum security and reliability
SFTP To Go offers managed cloud storage service - highly available, reliable and secure. Great for companies of any size, any scale.
Try SFTP To Go for free!

Frequently asked questions

Why do online retailers benefit from file synchronization on Shopify?

Online retailers benefit from file synchronization as it facilitates the efficient management of product information, images, manuals, and features, enhancing the overall shopping experience and brand representation on Shopify.

How has Shopify evolved in 2023?

By 2023, Shopify has witnessed significant growth, with over 4 million merchants using the platform. The platform has seen an 11% increase in Subscription Solutions revenue and a 10% rise in Monthly Recurring Revenue (MRR), indicating more merchants are adopting Shopify and utilizing its apps.

What makes Shopify a preferred choice for businesses?

Shopify is preferred for its user-friendly interface, comprehensive management features including inventory and order processing, and the ability to integrate external platforms and apps to enhance store functionality and traffic.

How has the management of store files on Shopify improved?

Since the June 2021 Update of the Shopify API, store files can now be created programmatically using the GraphQL API, streamlining the management process that was previously manual and tedious.

Why was SFTP To Go chosen for file import?

SFTP To Go was chosen for its seamless integration with Heroku, ease of deployment, and fully managed FTP Server, allowing developers to focus on coding while ensuring the efficient transfer of files.

How does SFTP To Go facilitate file import into Shopify?

SFTP To Go supports publicly served files from a special folder, enabling Shopify to fetch these files easily. Combined with services like FileOrganizerService, SftpFileLoaderService, and FileImporterService, it automates the file import process efficiently.

Can the file import process be further optimized?

Yes, the process can be extended to a full-fledged synchronization mechanism. SFTP To Go offers webhook notifications, enabling real-time file sync or removal from Shopify as soon as it’s uploaded.

What is Eshop Guide’s approach to work environment and employee wellbeing?

Eshop Guide focuses on reducing stress for employees and prioritizes physical and psychological wellbeing as part of a regular workday, believing that this approach ultimately increases overall productivity.