Hellođź‘‹Â Everyone,
The next few blog posts will be more of a brief overview of features I’ve built. If you have any questions just leave me a comment below. This was a very challenging feature to engineer, but I really enjoyed the learning that it provided.
In this post, you’ll see how I built out a CSV File Upload feature inside my existing Ember/Rails app. This blog post assumes you have previous experience in both Emberjs & Rails. This post will not show you how to set up an ember/rails app. It also assumes that your app is built using the JSON API Gem in rails. So moving forward everything I reveal will be under the umbrella of the JSON API Specs. For more information on JSON API RESOURCES http://jsonapi-resources.com
Also, if you see something that I left out, please leave a comment and I can update this post.
In Emberjs
Ember CLI Version 2.16.2 | Ember Data 2.16.2
Setup/Installs
• npm install –save ember-cli-uploader
• Ember generate ember-cli-uploader
• ember g component s3-upload
Build Out Component
app/components/csv-upload.js
import EmberUploader from ’ember-uploader’;import config from ‘../config/environment’;export default EmberUploader.FileField.extend({onComplete: ‘onComplete’,onProgress: ‘onProgress’,didError: ‘didError’,url: config.DS.host + “/post/sign/csv”,// video tutorial for connecting s3filesDidChange: function(filesDidChange) {let uploadUrl = this.get(‘url’),_this = this,uploader = EmberUploader.S3Uploader.create({url: uploadUrl,});uploader.on(‘didUpload’, function(response) {let res =$(response),fullUrl = decodeURIComponent(res.find(‘Location’)[0].textContent),key = decodeURIComponent(res.find(‘Key’)[0].textContent)_this.sendAction(‘onComplete’, {fullUrl: fullUrl, key: key});});uploader.on(‘progress’, function(e) {_this.sendAction(‘onProgress’, e.percent);})uploader.on(‘didError’, function(e) {_this.sendAction(‘didError’, e.responseJSON.errors[0].detail);})if (!Ember.isEmpty(filesDidChange))uploader.upload(filesDidChange[0]);}.observes(‘filesDidChange’)});
configure upload
/controller.js
import Ember from ’ember’;export default Ember.Controller.extend({csvUploadPercentage: null,errorMessage: null,actions: {csvUploadComplete(details) {let extension = details.key.split(‘.’)[1];let profile_pic = details.key;let newData = {profilePicExtension: extension,profilePicUrl: profile_pic,};let csv_upload = this.store.createRecord(‘DATA_MODEL_GOES_HERE‘, newData);csv_upload.save();this.transitionToRoute(‘ROUTE.TO.CERTAIN.PAGE‘);},csvUploadProgress(e) {Ember.run.once(this, function() {this.set(“csvUploadPercentage”, Math.round(e));});},errorMessage(e) {alert(e);this.transitionToRoute(‘profile.details’, this.model.id);}}});
/template.hbs
Upload Enterprise Records
{{csv-uploadid=”new-pic”onComplete=”csvUploadComplete”    onProgress=”csvUploadProgress” didError=”errorMessage”}}{{#if csvUploadPercentage}}
{{csvUploadPercentage}}%</div>{{/if}}</div></div>
Style it however you want.
Also, make sure you have a data model created inside the /models directory.
Connect config/environment.js file
/* eslint-env node */‘use strict’;module.exports = function(environment) {let ENV = {DS: {// localhosthost: ‘http://localhost:3000’},modulePrefix: ’ember-folder’,environment,rootURL: ‘/’,locationType: ‘auto’,EmberENV: {FEATURES: {// Here you can enable experimental features on an ember canary build// e.g. ‘with-controller’: true},EXTEND_PROTOTYPES: {// Prevent Ember Data from overriding Date.parse.Date: false}},APP: {// Here you can pass flags/options to your application instance// when it is created},’ember-simple-auth’: {authorizer: ‘authorization:token’},’ember-simple-auth-token’: {identificationField: ’email’,passwordField: ‘password’,headers: {‘Content-Type’: ‘application/vnd.api+json’,‘Accept’: ‘application./vnd.api+json’},refreshAccessTokens: false,}};if (environment ===’development’) {// ENV.APP.LOG_RESOLVER = true;// ENV.APP.LOG_ACTIVE_GENERATION = true;// ENV.APP.LOG_TRANSITIONS = true;// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;// ENV.APP.LOG_VIEW_LOOKUPS = true;}if (environment ===’test’) {// Testem prefers this…ENV.locationType = ‘none’;// keep test console output quieterENV.APP.LOG_ACTIVE_GENERATION = false;ENV.APP.LOG_VIEW_LOOKUPS = false;ENV.APP.rootElement = ‘#ember-testing’;}if (environment ===’production’) {// here you can enable a production-specific feature}ENV[’ember-simple-auth-token’].serverTokenEndpoint = `${ENV.DS.host}/session`;return ENV;};
In Rails
Rails Version 5.1.6 | Ruby Version 2.5.3
Setup/Installs
• add gem ‘aws-sdk’, ‘~> 0.1’
Add S3 Variables to env file
/.env-example
AWS_ACCESS_KEY_ID= ‘ADD_YOUR_ACCESS_KEY_HERE’
AWS_SECRET_ACCESS_KEY= ‘ADD_YOUR_SECRET_KEY_HERE’
AWS_REGION= ‘ADD_YOUR_REGION’
AWS_BUCKET= ‘ADD_YOUR_BUCKET’
Configure backend
/controllers/
require ‘aws-sdk’require’csv’class NAMEOFYOURController < ApplicationControllerdef csv# Step 1 – Connect to s3s3 = Aws::S3::Client.new# Step 2 – copy file into backendFile.open(‘FILE_NAME_GOES_HERE.csv’, ‘wb’) do |file|reap = s3.get_object({ bucket:ENV[‘AWS_BUCKET’], key:’BUCKET_DIRECTORY/FILE_NAME_GOES_HERE.csv’ }, target: file)end# Step 4 – upload csv file into dbCSV.foreach(‘FILE_NAME_GOES_HERE.csv’, :headers => true) do |row|DATABASE_TABLE_NAME_GOES_HERE.create(row.to_hash)end# Step 5 – Delete existing csv file from backendFile.delete(‘FILE_NAME_GOES_HERE.csv’)# Step 6 – Send an email to development notifiying them of the file uploadedmail = FILE_NAME_GOES_HEREMailer.Notify_developers(params)mail.deliver_nowrender json: {“data”: {“status”: “Finished!”}}enddef signsize = params[:size].to_imax_size = size.between?(0,2000000)extension = params[:type].split(“/”).lastvalid_extensions = [‘csv’, ‘vnd.ms-excel’]match = extension.end_with?(valid_extensions[0], valid_extensions[1])@expires = 10.hours.from_now.utc if match && max_sizerender json: signature if match && max_sizerender :status => :not_acceptable, json: {errors: [{status:406,title:”Not Acceptable”,detail:”Warning! Image needs to be a valid ‘csv’ format.”}]} if !matchrender :status => :not_acceptable, json: {errors: [{status:406,title:”Not Acceptable”,detail:”File size is more than 2MB.”}]} if !max_sizeenddef signatureclient = Aws::S3::Client.newresource = Aws::S3::Resource.new(client: client)bucket = resource.bucket ENV[‘AWS_BUCKET’]upload_path = ‘BUCKET_DIRECTORY/’ + params[:name]presigned_post = bucket.presigned_post(acl:’public-read’,expires: @expires,key: upload_path,policy: policy,success_action_status:’201′,content_disposition:’attachment’, # indicate this should be downloaded no opened in the browsercontent_type: params[:type],cache_control:’max-age=630720000, public’)fields = presigned_post.fieldsfields[‘bucket’] = ENV[‘AWS_BUCKET’]fieldsenddef policy(options = {})Base64.strict_encode64({expiration: @expires,conditions: [{ bucket: ENV[‘AWS_BUCKET’] },{ acl: ‘public-read’ },{ expires: @expires },{ success_action_status: ‘201’ },[ ‘starts-with’, ‘$key’, ” ],[ ‘starts-with’, ‘$Content-Type’, ” ],[ ‘starts-with’, ‘$Cache-Control’, ” ],[ ‘content-length-range’, 0, 524288000 ]]}.to_json)endend
config/routes.rb
# CSV Uploadpost “/MODEL_NAME_GOES_HERE“, to: “CONTROLLER_NAME_GOES_HERE#csv”get “/post/sign/csv”, to: “CONTROLLER_NAME_GOES_HERE#sign”
Credits: