I am attempting to implement a flask application for uploading files. This file could be very large. For example, almost 2G in size.
I have finished the server side process function like this:
#app.route("/upload/<filename>", methods=["POST", "PUT"])
def upload_process(filename):
filename = secure_filename(filename)
fileFullPath = os.path.join(application.config['UPLOAD_FOLDER'], filename)
with open(fileFullPath, "wb") as f:
chunk_size = 4096
while True:
chunk = flask.request.stream.read(chunk_size)
if len(chunk) == 0:
return
f.write(chunk)
return jsonify({'filename': filename})
As for browser side, I should give users a from to submit the file. One file at a time. Show progressbar to indicate the uploading process.
But I have no idea about the browser side code. How can I use javascript code to start the uploading and show it status?
This will be a difficult task for your to figure out on your own. I would suggest a plugin like https://blueimp.github.io/jQuery-File-Upload/
You can see from this projects source code they use a method name which essentially looks at how large the file is and how much data has been transferred thus far and how much remains to show a percentage complete div.
code example from this project
progressall: function (e, data) {
var $this = $(this);
$this.find('.fileupload-progress')
.find('.progress').progressbar(
'option',
'value',
parseInt(data.loaded / data.total * 100, 10)
).end()
.find('.progress-extended').each(function () {
$(this).html(
($this.data('blueimp-fileupload') ||
$this.data('fileupload'))
._renderExtendedProgress(data)
);
});
}
https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.fileupload-jquery-ui.js
So if you do want to come up with your own solution, I would suggest you start by building a UI div rectangle which has a dynamic width which updates according to your percentage calculation based upon the file upload size and data uploaded... or just go with an already established solution.
Related
I have an HTML page that lets you upload a picture and displays it (very simple). But how do I send that particular loaded image into my flask app so that I could, say, perform some image processing on it using python's openCV or PIL packages?
Disclaimer
I've done similar thing recently, It may not be a 100% solution for you, but using parts of this code will propably solve all of your problems.
Flask Code
This part gets all files uploaded from browser and saves them with their respected extensions.
if request.method=="PUT":
for f in request.files.getlist("Files"): #<---- note 1
f.save(os.path.join(Path_To_Folder, f.filename))
return jsonify({"result": "res"}), 200 #status code is only thing important here
HTML
This just allows you to select file(s) from your device
<input type="file" id="file_loader" multiple/>
JavaScript
This code
const file_loader = document.getElementById("file_loader");
file_loader.onchange = async function(e){
alert("Upload started")
let sending = new FormData(); //creates form data object
//this for loop adds all files you selected to a form object
for (let i = 0; i < file_loader.files.length; i++) {
sending.append("Files", file_loader.files[i]); //<---- note 1
}
//this part just sends all the files to server
const podaci = await fetch(url_filesistem, {
method: "PUT",
body: sending,
})
.then((response) => response.json())
.then((pod) => {return pod;});
//removing all selected files from an input, so every time you want to select files
//its just those files, not ones previously selected
while (file_loader.length > 0) {
file_loader.pop();
}
alert("Finnished uploading")
}
Note 1
String "Files" mentioned in both lines needs to be the same in order for this method to work.
Advice
First save all the files on the server and then do the processing. I don't know what "f" object from for loop in python contains, therefore I don't know whether you can process them immediately.
Feel free to ask me anything regarding my code!
Best regards!
The use case: I'm developing a web app to help students learn to read. The student is recorded while reading a text on a web app. The signal is sent by segment of 200ms to the backend and analysed before the student finishes reading to give live feedback during the reading. The server will send feedback after each segment analysis.
On the web app the code looks like this:
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream)
mediaRecorder.start(200)
mediaRecorder.ondataavailable = event => {
socket.emit('my_event', Blob([event.data]))
}
})
On chrome, the media type produced is webm. I'm wondering how to handle the data on the backend so that I can analyse the media with numpy before the end of the recording.
For now I couldn't find a better way than something like:
from pydub import AudioSegment
def blobToSignal(blob, is_first_sequence):
webm_header = b'\x1aE...'
fp = tempfile.NamedTemporaryFile()
fp.write(blob) if is_first_sequence else fp.write(webm_header + blob)
fp.seek(0)
samples = AudioSegment.from_file(fp.name, 'webm').get_array_of_samples()
fp.close()
return samples # this is almost a numpy array (analyzable)
I tried to change the front to return a Float32Array instead of a webm:
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(stream => {
audio_context = new AudioContext()
var audioInput = audio_context.createMediaStreamSource(stream)
var recorder = audio_context.createScriptProcessor(8096, 1, 1)
recorder.onaudioprocess = event => {
socket.emit(
'my_event',
Array.from(event.inputBuffer.getChannelData(0))
)
}
audioInput.connect(recorder)
recorder.connect(audio_context.destination)
So that the backend can use the raw signal but this method requires a too high bandwidth (~1Mb/s).
So my questions are:
am I doing something wrong here?
is there a Python librairy to decode a webm coming from a Buffer? (or something similar? I'm not so familiar with Python...)
how would you handle this use case?
Thanks for your help!
I want to stream the audio of my microphone (that is being recorded via pyaudio) via Flask to any client that connects.
This is where the audio comes from:
def getSound(self):
# Current chunk of audio data
data = self.stream.read(self.CHUNK)
self.frames.append(data)
wave = self.save(list(self.frames))
return data
Here's my flask-code:
#app.route('/audiofeed')
def audiofeed():
def gen(microphone):
while True:
sound = microphone.getSound()
#with open('tmp.wav', 'rb') as myfile:
# yield myfile.read()
yield sound
return Response(stream_with_context(gen(Microphone())))
And this is the client:
<audio controls>
<source src="{{ url_for('audiofeed') }}" type="audio/x-wav;codec=pcm">
Your browser does not support the audio element.
</audio>
It does work sometimes, but most of the times I'm getting "[Errno 32] Broken pipe"
When uncommenting that with open("tmp.wav")-part (the self.save() optionally takes all previous frames and saves them in tmp.wav), I kind of get a stream, but all that comes out of the speakers is a "clicking"-noise.
I'm open for any suggestions. How do I get the input of my microphone live-streamed (no pre-recording!) to a webbrowser?
Thanks!
Try This its worked for me. shell cmd "cat" is working perfect see the code
iam using FLASK
import subprocess
import os
import inspect
from flask import Flask
from flask import Response
#app.route('/playaudio')
def playaudio():
sendFileName=""
def generate():
# get_list_all_files_name this function gives all internal files inside the folder
filesAudios=get_list_all_files_name(currentDir+"/streamingAudios/1")
# audioPath is audio file path in system
for audioPath in filesAudios:
data=subprocess.check_output(['cat',audioPath])
yield data
return Response(generate(), mimetype='audio/mp3')
This question was asked long time ago, but since I spent entire day to figure out how to implement the same, I want to give the answer. Maybe it will be helpful for somebody.
"[Errno 32] Broken pipe" error comes from the fact that client can not play audio and closes this stream.
Audio can not be played due to absence of the header in the data stream. You can easily create the header using genHeader(sampleRate, bitsPerSample, channels, samples) function from the code here . This header has to be attached at least to the first chunck of sent data ( chunck=header+data ). Pay attention, that audio can be played ONLY untill client reaches file size in download that you have to specify in the header. So, workaround would be to set in the header some big files size, e.g. 2Gb.
Instead of datasize = len(samples) * channels * bitsPerSample in the header function write datasize = 2000*10**6.
def gen_audio():
CHUNK = 512
sampleRate = 44100
bitsPerSample = 16
channels = 2
wav_header = genHeader(sampleRate, bitsPerSample, channels)
audio = AudioRead()
data = audio.get_audio_chunck()
chunck = wav_header + data
while True:
yield (chunck)
data = audio.get_audio_chunck()
chunck = data
After lots research and tinkering I finally found the solution.
Basically it came down to serving pyaudio.paFloat32 audio data through WebSockets using Flask's SocketIO implementation and receiving/playing the data in JavaScript using HTML5's AudioContext.
As this is requires quite some code, I think it would not be a good idea to post it all here. Instead, feel free to check out the project I'm using it in: simpleCam
The relevant code is in:
- noise_detector.py (recording)
- server.py (WebSocket transfer)
- static/js/player.js (receiving/playing)
Thanks everyone for the support!
I have one function view that creates a report using xlsxwriter, it is created on the fly using a StringIO as buffer and finally sending through HttpResponse. It works well using Local Server.
The problem is that on Heroku, after some seconds (documentation mention 30 seconds timeout and not modifiable) the server hangs out and reboot the web process, giving error as a response.
What is the best way to...?:
create an xmlx file on the fly (dynamically) in memory
serve the entire file to the client.
prevent server to hang out because of the long process running
This is a piece of the code I am using:
def reporte_usuarios(request):
from xlsxwriter.workbook import Workbook
try:
import cStringIO as StringIO
except ImportError:
import StringIO
# create a workbook in memory
output = StringIO.StringIO()
workbook = Workbook(output)
bold = workbook.add_format({'bold': True})
# get the data
from django.db.models import Count
usuarios = User.objects.filter(....... # all filter stuff
for usr in usuarios:
if usr.activos > 0:
# create a workbook sheet every User registered
ws = workbook.add_worksheet(u'%s' % usr.username)
# some relevant user data
ws.write(1, 1, u'USUARIO: %s' % usr.username)
...
# get rows for user
log = LogActivos.objects.filter(usuario=usr).select_related('activo__unidad__id', 'activo__unidad__nombre', 'activo__nombre')
# write headers
ws.write(3, 0, u'FECHA', bold)
...
sig_fila = 4 #starting row for data (after headers)
for l in log:
# write all data
ws.write(sig_fila, 0, u'%s' % l.fecha)
...
sig_fila += 1
# close the workbook
workbook.close()
# go to the beginning of the buffer
output.seek(0)
# response using the buffer
response = HttpResponse(output.read(), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename="ACTIVOS_USUARIOS__%s.xlsx"' % datetime.now().strftime("%Y%m%d_%H%M")
return response
Notes: I am using Gunicorn on Heroku, django 1.9.13 and python 2.7.11
IMHO you should follow a totally different approach in this case.
As you are generating a file rather big in size, it's normal for the system to hang out because of a timeout error.
What you could do instead is to deploy a background task queue, like Celery or DjangoRQ. With that, you will get a background task to create this file using your user's data, and then you can let your user know that it's ready by any mean, like a notification or an email.
If you need more details regarding how you can do something like this, let me know and I can help :)
How to restrict the size of file being uploaded.
I am using django 1.1 with apache.
Can I use apache for this and show some html error page if say size is bigger then 100MB.
Thanks.
I mean before uploading the file
On client side it isn't possible...
I suggest to write a custom upload handlers and to override receive_data_chunk.
Example: QuotaUploadHandler
You can do this in javascript in most recent browsers, using the File API: http://www.w3.org/TR/FileAPI/
For example (using jquery):
var TYPES = ['image/jpeg', 'image/jpg', 'image.png'];
var file = $('#my_file_input')[0].files[0];
var size = file.size || file.fileSize;
var type = file.type;
if (size > MAX_BYTES) {
alert('Error: file too large');
} else if (TYPES.indexOf(type) < 0) {
alert('Error: file not a JPG or PNG');
} else {
// proceed with file upload
}
No need for Java or Flash. Of course, you'll still need some sort of checking on the server for users who disable javascript.
apache has a server setting for max file size..(also dont forget max post size). I do not believe apache can show an error page on its own, you can probably use python for that.
unfortunetly I know nothing obout python (yet) so I can't really help you beyond that.
I know php can do that easily so I'm sure there is a method for python.
If you want to get the file size before uploading begins you will need to use Flash or a Java applet.
Writing a custom upload handler is the best approach. I think something like the following would work (untested). It terminates the upload as early as possible.
from django.conf import settings
from django.core.files.uploadhandler import FileUploadHandler, StopUpload
class MaxSizeUploadHandler(FileUploadHandler):
"""
This test upload handler terminates the connection for
files bigger than settings.MAX_UPLOAD_SIZE
"""
def __init__(self, request=None):
super(MaxSizeUploadHandler, self).__init__(request)
def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
if content_length > settings.MAX_UPLOAD_SIZE:
raise StopUpload(connection_reset=True)