Hello Everyone,
The target audience of this post is limited.
In this article of mine, I will write down the steps of how you can connect with Box Explorer Component to allow users to upload file into your Box account.
Requirement: We want to allow our users to upload file to somewhere in the cloud where we (me and my team) can see the files and take further actions on it
There are many solutions to this: You have AWS S3, DropBox, Google Cloud and lots more but if you do not want to spend much time on designing and engineering the front-end (by designing I mean building the entire UI for file management), you can use Box.com’s easy to integrate Box Explorer Component.
Related links:
- https://developer.box.com/guides/embed/ui-elements/explorer/
- https://app.box.com/developers/console
- https://app.box.com/master
- https://github.com/box-community/samples-docs-authenticate-with-jwt-api/blob/master/sample.rb
Prerequisites:
- Ruby >= 2
- Rails >= 5
- A Box.com account
- A Box.com App – You can create the same from Developer’s console (see Related links#2)
- Make sure your app is authorised. You can do the same from here: https://app.box.com/master/custom-apps
- A config.json file that you can download from: https://app.box.com/developers/console/app/:app_id/configuration. You’ll have to generate Public/Private keypair for this – You can do the same from the same page.
- You’ll have to add your domain to allowed origins via above link.
Sample Output / Demo: https://codepen.io/box-platform/pen/wdWWdN. In addition you’ll also see options to add / upload file / folder and perform operations like Search for uploads, Download, Share, Delete etc.
Once you are done setting up above things, let’s code this:
# lib/box_api.rbrequire 'json'require "openssl"require 'securerandom'require 'jwt'require 'json'require 'uri'require 'net/https'class BoxApi def initialize unique_identifier = nil @unique_identifier = unique_identifier @access_token = Rails.cache.fetch("users/#{@unique_identifier}/box/access_token") end def get_token return @access_token unless @access_token.nil? set_auth # Make the request uri = URI.parse(@authentication_url) http = Net::HTTP.start(uri.host, uri.port, use_ssl: true) request = Net::HTTP::Post.new(uri.request_uri) request.body = @params response = http.request(request) # Parse the JSON and extract the access token @access_token = Rails.cache.fetch("users/#{@unique_identifier}/box/access_token", expires_in: 60.minutes) do JSON.parse(response.body)['access_token'] end return @access_token end def find_or_create_folder_by_folder_name folder_name = @unique_identifier root_folder = find_folder "Client Uploads" root_folder = create_folder "Client Uploads" if !root_folder child_folder = find_folder folder_name, root_folder["id"] child_folder = create_folder folder_name, root_folder["id"] if !child_folder return child_folder["id"] end def find_folder folder_name, parent_folder_id = nil base_url = "https://api.box.com/2.0/search?query='#{folder_name}'&type=folder" base_url += "&ancestor_folder_ids=#{parent_folder_id}" if parent_folder_id uri = URI.parse(base_url) request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer #{@access_token}" req_options = { use_ssl: uri.scheme == "https", } response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http| http.request(request) end response_body = JSON.parse(response.body) return false if response_body["total_count"] == 0 && response_body["entries"].empty? return response_body["entries"][0] end def create_folder folder_name, parent_folder_id = nil uri = URI.parse("https://api.box.com/2.0/folders") request = Net::HTTP::Post.new(uri) request.content_type = "application/json" request["Authorization"] = "Bearer #{@access_token}" params = {} params["name"] = folder_name params["parent"] = { "id" => parent_folder_id ? parent_folder_id : "0" } request.body = JSON.dump(params) req_options = { use_ssl: uri.scheme == "https", } response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http| http.request(request) end response_body = JSON.parse(response.body) return response_body if response.code == "201" endprivate def set_auth @config = JSON.parse( File.read("#{Rails.root.join}/config/box_config.json") ) @config = Rails.env.production? ? @config["production"] : @config["base"] @appAuth = @config['boxAppSettings']['appAuth'] key = OpenSSL::PKey::RSA.new( @appAuth['privateKey'], @appAuth['passphrase'] ) @authentication_url = 'https://api.box.com/oauth2/token' claims = { iss: @config['boxAppSettings']['clientID'], sub: @config['enterpriseID'], box_sub_type: 'enterprise', aud: @authentication_url, # This is an identifier that helps protect against # replay attacks jti: SecureRandom.hex(64), # We give the assertion a lifetime of 45 seconds # before it expires exp: Time.now.to_i + 45 } keyId = @appAuth['publicKeyID'] # Rather than constructing the JWT assertion manually, we are # using the pyjwt library. # The API support "RS256", "RS384", and "RS512" encryption assertion = JWT.encode(claims, key, 'RS512', { kid: keyId }) # We are using the excellent axios package # to simplify the API call @params = URI.encode_www_form({ # This specifies that we are using a JWT assertion # to authenticate grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', # Our JWT assertion assertion: assertion, # The OAuth 2 client ID and secret client_id: @config['boxAppSettings']['clientID'], client_secret: @config['boxAppSettings']['clientSecret'] }) endend
Let’s call this api via controller’s method: `/app/controllers/home_controller.rb`
# /app/controllers/home_controller.rbclass HomeController < ApplicationController def connect_to_box api = BoxApi.new(user.company.name) token = api.get_token folder_id = api.find_or_create_folder_by_folder_name respond_to do | wants | wants.json do render json: { success: true, access_token: token, folder_id: folder_id } end end endend
and finally let’s load token and folder_id via Ajax call from view
/app/views/home/files.html.erb<%= content_for :page_css do %> <link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/elements/13.0.0/en-US/explorer.css" /><% end %><%= content_for :page_js do %> <!-- polyfill.io only loads the polyfills your browser needs --> https://cdn.polyfill.io/v2/polyfill.min.js?features=es6,Intl https://cdn01.boxcdn.net/platform/elements/13.0.0/en-US/explorer.js<% end %><div class=<%= "#{controller_name}_#{action_name}" %>> <div id="uploader-panel" class="box-light-blue"> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="text-center result-overlay"> <%= image_tag 'pageloader.svg' %> </div> <div class="box-list-container" style="width: 100%; height: 100%; margin: 0; padding: 0; min-width: 320px;"></div> </div> </div> </div> </div></div><script type="text/javascript"> $(document).ready(function() { //connecting to box to fetch required token and folder $.ajax({ url: '/home/connect_to_box', type: 'POST', dataType: 'json', success: function(response) { if(response['success'] === true){ $("div.result-overlay").hide(); const folderId = response['folder_id']; const accessToken = response['access_token']; const logoUrl = "<%= asset_path('logo-blue.jpg') %>"; const contentExplorer = new Box.ContentExplorer(); // for more options visit: https://developer.box.com/guides/embed/ui-elements/uploader/#options contentExplorer.show(folderId, accessToken, { container: ".box-list-container", canDownload: true, canUpload: true, canCreateNewFolder: true, sortBy: "date", sortDirection: "DESC", logoUrl: logoUrl, onClose: null }); contentExplorer.on('upload', (data) => { //notify via slack $.ajax({ url: '/home/upload_notify', type: 'POST', beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))}, dataType: "json", data: {files: JSON.stringify(data)} }); }); contentExplorer.on('error', (data) => { alert(`Error uploading file with name "${data.file.name}". The error was: "${data.error}"`); }); } } }) });</script>
Here’s what’s happening:
- When the page loads we are firing Ajax call to get
access_tokenandfolder_idfrom Box.folder_idis the ID of the folder where the user will upload all files. We are giving different folder for each user who belongs to different company (see table below). - The call to Box API checks if the access token is present for the company, it will return that else it will connect to Box, get the access token, set the cache and return the token – Important thing to note that the token returned from Box is valid for 60 minutes
- Based on the returned token, we will check if the folder is present for the company’s user or not!
- The last part of the Ajax call I have written will send the slack notification.
| User | Company Name | Folder on Box |
| User-A | ABC | ABC |
| User-B | XYZ | XYZ |
| User-C | ABC | ABC |
| User-D | A123 | A123 |
That’s it. Hook it up and see it in action.

Leave a comment