React.js + SuperAgentでファイルをアップロード

File APIを使用してSuperAgentでファイルをアップロードするサンプルです。

### React Views
views/file_uploader.jsx

```jsx:file_uploader.jsx
var React = require('react/addons');
var UploadFileActionCreator = require('../actions/upload_file_action_creator');

var FileUploader = React.createClass({
    getInitialState: function() {
        return {
            dragging: false
        };
    },
    uploadFiles: function(files) {
        var formData = new FormData();

        for (var i = 0, f; f = files[i]; ++i) {
            formData.append('file', f);
            UploadFileActionCreator.upload(formData);
        }
    },
    handleDragEnter: function(e) {
        e.stopPropagation();
        e.preventDefault();

        this.setState({dragging: true});
    },
    handleDragOver: function(e) {
        e.stopPropagation();
        e.preventDefault();
    },
    handleDragLeave: function(e) {
        this.setState({dragging: false});
    },
    handleDrop: function(e) {
        e.stopPropagation();
        e.preventDefault();

        this.setState({dragging: false});

        var files = e.dataTransfer.files;

        this.uploadFiles(files);
    },
    handleClick: function(e) {
        this.refs.file.getDOMNode().click();
    },
    handleChangeFile: function(e) {
        var files = e.target.files;

        this.uploadFiles(files);
    },
    render: function() {
        var className = this.state.dragging ? 'app-file-selection-area-dragging' : 'app-file-selection-area';

        var element =
            <div className={className}>
                <div className="app-drop-area" onDrop={this.handleDrop} onDragEnter={this.handleDragEnter} onDragOver={this.handleDragOver} onDragLeave={this.handleDragLeave} >
                    <p>アップロードするファイルをドロップ</p>
                    <p>または</p>
                    <input type="file" className="app-upload-file" ref="file" onChange={this.handleChangeFile} />
                    <button onClick={this.handleClick}>ファイルを選択</button>
                </div>
            </div>

        return (element);
    }
});

module.exports = FileUploader;
```

### Action Creators
actions/upload_file_action_creator.js

```js:upload_file_action_creator.js
var UploadAPIUtil = require('../api_utils/upload_api_util');
var config = require('../config/config');

var UploadFileActionCreator = {
    upload: function(formData) {
        var url = config.fileUploadURL;
        UploadAPIUtil.upload(formData, url, function(err, res) {
            if (err) {
                console.log(err);
            } else {
                console.log(res);
            }
        });
    },
};

module.exports = UploadFileActionCreator;
```

### Web API Utils
api_utils/upload_api_util.js

```js:upload_api_util.js
var request = require('superagent-bluebird-promise');
var Promise = require('bluebird');

var UploadAPIUtil = {
    upload: function(formData, url, callback) {
        var promise;

        promise = request
            .post(url)
            .send(formData)
            .promise()
            .then(function(res) {
                if (callback) {
                    callback(null, JSON.parse(res.text));
                }
            })
            .catch(function(err) {
                if (callback) {
                    callback(err);
                }
            });
    },
};

module.exports = UploadAPIUtil;
```

こちらの記事を参考にさせていただきました。
http://soraxism.com/soraxism/blog/html5%E3%81%AEfile-api%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E3%80%81%E3%83%89%E3%83%A9%E3%83%83%E3%82%B0%EF%BC%86%E3%83%89%E3%83%AD%E3%83%83%E3%83%97%E3%81%A7%E3%83%95%E3%82%A1%E3%82%A4