Commit ff855182 authored by charlyjazz's avatar charlyjazz
Browse files

Create components for file upload and file information

parent 1822cbd8
{
"presets": [
"env",
"react",
"stage-1"
],
"plugins": [
"transform-class-properties",
"transform-decorators"
]
}
\ No newline at end of file
/node_modules/
static/javascript/bin/bundle.js
/env/
/.idea/
package-lock.json
*pyc
\ No newline at end of file
from flask_socketio import SocketIO
from flask import Flask
app = Flask(__name__)
app.config['FILEDIR'] = 'static/files/'
socketio = SocketIO(app)
\ No newline at end of file
from . import socketio
from flask import current_app
import os, uuid, json
@socketio.on('start-transfer', namespace='/uploads')
def start_transfer(filename, size):
"""Process an upload request from the client."""
_, ext = os.path.splitext(filename)
if ext in ['.exe', '.bin', '.js', '.sh', '.py', '.php']:
return False # reject the upload
id = uuid.uuid4().hex # server-side filename
with open(current_app.config['FILEDIR'] + id + '.json', 'wt') as f:
json.dump({'filename': filename, 'size': size}, f)
with open(current_app.config['FILEDIR'] + id + ext, 'wb') as f:
pass
return id + ext # allow the upload
@socketio.on('write-chunk', namespace='/uploads')
def write_chunk(filename, offset, data):
"""Write a chunk of data sent by the client."""
if not os.path.exists(current_app.config['FILEDIR'] + filename):
return False
try:
with open(current_app.config['FILEDIR'] + filename, 'r+b') as f:
f.seek(offset)
f.write(data)
except IOError:
return False
return True
\ No newline at end of file
{
"name": "flask-socketio-react-fileupload",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch": "node_modules/.bin/webpack --watch"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-react": "^6.24.1",
"css-loader": "^0.28.7",
"style-loader": "^0.19.0",
"webpack": "^3.8.1"
},
"dependencies": {
"babel-preset-airbnb": "^2.4.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-1": "^6.24.1",
"rc-progress": "^2.2.2",
"react": "^16.0.0",
"react-dom": "^16.0.0"
}
}
click==6.7
Flask==0.12.2
Flask-SocketIO==2.9.2
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==1.0
pkg-resources==0.0.0
python-engineio==1.7.0
python-socketio==1.8.1
six==1.11.0
Werkzeug==0.12.2
gevent==1.2.2
gevent-websocket==0.10.1
greenlet==0.4.12
from flask import render_template
from . import app
@app.route('/')
def index():
return render_template('index.html')
\ No newline at end of file
#!/bin/sh
source bin/activate
pip install -r requirements.txt
export FLASK_DEBUG=1
export FLASK_APP=server.py
flask run
\ No newline at end of file
html {
font-size: 16px;
}
.Drop-input {
width: 100%;
height: 60px;
background-color: #ddd;
border: 4px dotted #888;
text-align: center;
padding-top: 40px;
}
.Div-files .File:first-child {
padding-top: 1em;
border-top: 2px solid #37989291;
}
.File {
margin-top: 1em;
margin-bottom: 1em;
padding-bottom: 1em;
border-bottom: 2px solid #37989291;
}
.File-type {
margin-left: 2em;
}
.Div-Button {
text-align: center;
margin-top: 2em;
margin-bottom: 2em;
}
.Div-Button button {
padding: 1em;
background: #87bcde;
border: none;
box-shadow: 0px 1px 15px 1px #0000004f;
cursor: pointer;
}
\ No newline at end of file
import React, { Component } from 'react';
import './App.css';
import { Line } from 'rc-progress';
import DropFileInput from './components/DropFileInput'
class App extends Component {
constructor(props) {
super(props);
this.state = {
progress: 0
}
}
progressDown = () => {
this.setState({
progress: this.state.progress - 1
})
};
progressUp = () => {
this.setState({
progress: this.state.progress + 1
})
};
render() {
return (
<div className="App">
<DropFileInput />
<Line percent={this.state.progress}
strokeWidth="1"
strokeColor="#48bd96"
strokeLinecap='square'
trailColor="#2fb9e224"/>
<button onClick={this.progressUp}>Up</button>
<button onClick={this.progressDown}>Down</button>
</div>
);
}
}
export default App;
// TODO: var socketio = require('socket.io-client')('http://' + document.domain + ':' + location.port);
function readFileChunk (file, offset, length, success, error) {
let end_offset = offset + length;
if (end_offset > file.size)
end_offset = file.size;
let r = new FileReader();
r.onload = function(file, offset, length, event) {
if (event.target.error != null)
error(file, offset, length, event.target.error);
else
success(file, offset, length, event.target.result);
}.bind(r, file, offset, length);
r.readAsArrayBuffer(file.slice(offset, end_offset));
}
const onReadSuccess = (file, offset, length, data) => {
// Read success callback
if (this.state.done) return;
if (!socketio.connected) {
// The WebSocket connection was lost, wait until it comes back
setTimeout(onReadSuccess.bind(this, file, offset, length, data), 5000);
return;
}
socketio.emit('write-chunk', this.server_filename, offset, data, function(offset, ack) {
if (!ack) {
onReadError(this.file, offset, 0, 'Transfer aborted by server')
}
}.bind(this, offset));
let end_offset = offset + length;
this.state.progress.style.width = parseInt(300 * end_offset / file.size) + "px";
if (end_offset < file.size)
readFileChunk(file, end_offset, this.state.chunk_size, onReadSuccess.bind(this), onReadError.bind(this));
else {
this.state.progress.classList.add('complete');
this.state.progresss.classList.remove('in-progress');
this.state.done = true;
}
};
function onReadError(file, offset, length, error) {
// Read error callback
console.log('Upload error for ' + file.name + ': ' + error);
this.progress.classList.add('error');
this.progress.classList.remove('in-progress');
this.done = true;
}
module.exports = {
readFileChunk,
onReadSuccess,
onReadError
};
\ No newline at end of file
import React, { Component } from 'react';
import File from './File';
import { readFileChunk, onReadSuccess, onReadError } from '../actions'
export default class DropFileInput extends Component {
constructor(props) {
super(props);
this.state = {
chunk_size: 64 * 1024,
files: []
}
}
handlerOnDragOver = event => {
event.preventDefault();
};
handlerOnDrop = event => {
event.preventDefault();
for(let i = 0; i < event.dataTransfer.files.length; i++) {
this.setState({
files: [...this.state.files, ...[event.dataTransfer.files[i]]]
}, () => {console.log(this.state)} )
}
};
render() {
let files = this.state.files.map((file, index) => {
return <File file={file} key={index}/>
});
return (
<div>
<div className="Drop-input"
onDragOver={this.handlerOnDragOver}
onDrop={this.handlerOnDrop}>
Drop files here!
</div>
<div className="Div-files">
{files}
</div>
<div className="Div-Button">
<button>
Upload File{this.state.files.length > 1 && "s"}
</button>
</div>
</div>
)
}
}
\ No newline at end of file
import React, { Component } from 'react';
export default class File extends Component {
constructor(props) {
super(props);
this.state = {
done: false,
progress: 0
}
}
render() {
return (
<div className="File">
<span className="File-name">Name: {this.props.file.name}</span>
<span className="File-type">Type: {this.props.file.type}</span>
</div>
)
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>File Upload</title>
</head>
<body>
<div id="root"></div>
</body>
<script type=text/javascript src="{{url_for('static', filename='javascript/bin/bundle.js') }}"></script>
</html>
\ No newline at end of file
var path = require('path');
const ENTRY_DIR = path.resolve(__dirname, 'static/javascript/src');
var config = {
entry: ENTRY_DIR + '/index.js',
output: {
path: path.resolve(__dirname, 'static/javascript/bin'),
filename: 'bundle.js'
},
module : {
loaders : [
{
test : /\.js?/,
include : ENTRY_DIR,
loader : 'babel-loader'
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
};
module.exports = config;
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment