File upload with Ember data

2019-01-17 03:49发布

问题:

Can someone provide code examples or documentation on implementing a form with a file field using EmberJS and Ember Data?

I'm familiar with Ember Data already but I'm not sure how to implement file-uploads correctly.

回答1:

Here is part of an ember-data adapter I wrote to do file uploads (same server -not cross domain)

DS.DjangoRESTAdapter = DS.RESTAdapter.extend({
        bulkCommit: false,

        createRecord: function(store, type, record) {
            var root = this.rootForType(type), json = {};

            var data = new FormData();
            data.append('username', record.get('username'));
            data.append('attachment', record.get('attachment'));

            this.django_file_ajax('http://localhost:8000/people/new/', "POST", {
                data: data,
                context: this,
                success: function(pre_json) {
                    json[root] = pre_json;
                    this.didCreateRecord(store, type, record, json);
                }
            });
        },

        django_file_ajax: function(url, type, hash) {
            hash.url = url;
            hash.type = type;
            hash.contentType = false;
            hash.processData = false;
            hash.context = this;

            jQuery.ajax(hash);
        }

    });

})();

It's not IE8 friendly as is because I'm using the "FormData" helper to do multipart file upload but it's a good proof of concept.

Here is the ember-data model to go w/ the above adapter

PersonApp.Person = DS.Model.extend({
  id: DS.attr('number'),
  username: DS.attr('string'),
  attachment: DS.attr('string')
});

Here is the handlebars template

<script type="text/x-handlebars" data-template-name="person">
{{view PersonApp.UploadFileView name="logo_image" contentBinding="content"}}
</script>

Here is the custom ember view

PersonApp.PersonView = Ember.View.extend({
  templateName: 'person'
});

PersonApp.UploadFileView = Ember.TextField.extend({
    type: 'file',
    attributeBindings: ['name'],
    change: function(evt) {
      var self = this;
      var input = evt.target;
      if (input.files && input.files[0]) {
        var reader = new FileReader();
        var that = this;
        reader.onload = function(e) {
          var fileToUpload = e.srcElement.result;
          var person = PersonApp.Person.createRecord({ username: 'heyo', attachment: fileToUpload });
          self.get('controller.target').get('store').commit();
        }
        reader.readAsDataURL(input.files[0]);
      }
    }
});

If you want to see a full blown spike with this in action checkout a multi file upload example I did recently.

https://github.com/toranb/ember-file-upload



回答2:

Look at the links below. The first link or blog post contains a link to a working jsfiddle that handles upload with emberjs. Note I didn't write the blog or create the fiddle. But it should solve your issue.

http://chrismeyers.org/2012/06/12/ember-js-handlebars-view-content-inheritance-image-upload-preview-view-object-binding/ - dead link

http://devblog.hedtek.com/2012/04/brief-foray-into-html5-file-apis.html



回答3:

It's fairly simple, the general steps are:

  1. Hook up an input within Ember.
  2. Read the data from the local file specified in the input element.
  3. Encoded the data as base64.
  4. Set the value of your ember-data model to the base64 string.
  5. On the server, decode the base64 string and voila, your binary file data is on the server.

It should be noted though that base64 encoding large files has performance issues, but for smaller images or text it won't be a problem.


You could also send the file 'outside' of Ember Data, and push the response (such as a JSON payload representing a model) into the store via pushPayload. If so, FormData or other methods in XHR2 can be used.

Read more about client-side manipulation of files here: http://www.html5rocks.com/en/tutorials/file/dndfiles/

Read more about XHR2 and FormData for file uploads here: http://www.html5rocks.com/en/tutorials/file/xhr2/



回答4:

I tried a few different solutions and ended up writing a FormData adapter and a File transform. Then any model that needs to upload file data can just use the FormDataAdapter and define the file attributes as type "file":

app/transforms/file.coffee

FileTransform = DS.Transform.extend
  serialize: (jsonData) ->
    jsonData

  deserialize: (externalData) ->
    externalData

app/models/user.coffee

User = DS.Model.extend
    avatar: DS.attr('file')

app/adapters/form_data.coffee

get = Ember.get;

App.FormDataAdapter = ApplicationAdapter.extend
  ajaxOptions: (url, type, hash) ->
    hash = hash || {}
    hash.url = url
    hash.type = type
    hash.dataType = 'json'
    hash.context = @

    if hash.data and type != 'GET' and type != 'DELETE'
      hash.processData = false
      hash.contentType = false
      fd = new FormData()
      root = Object.keys(hash.data)[0]
      for key in Object.keys(hash.data[root])
        if hash.data[root][key]
          fd.append("#{root}[#{key}]", hash.data[root][key])

      hash.data = fd

    headers = get(@, 'headers')
    if headers != undefined
      hash.beforeSend = (xhr) ->
        for key in Ember.keys(headers)
          xhr.setRequestHeader(key, headers[key])

    hash

app/adapters/user.coffee

UserAdapter = FormDataAdapter.extend()

Sorry about the CoffeeScript, but it's pasted from this blog post: http://blog.mattbeedle.name/posts/file-uploads-in-ember-data/. You can read a more detailed description there. This solution should probably be combined with a HTML5 FileReader input to enable image previews and client side file type validation.



标签: ember.js