Saving an audio blob as a file in a rails app - javascript

I am using recorder.js and have successfully gotten to the point where I can record an audio snippet and play it back over and over again to my heart's content. But now I am trying to make a post of this "blob" (which I know contains proper data since it's playing back correctly) as an audio wav file in my /public folder. My issue is that I am not sure how to deal with the blob and actually post its content. This is my code:
function sendWaveToPost1() {
console.log(savedWAVBlob);
$.ajax({ url: '/worm/save',
type: 'POST',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
data: 'someData=' + savedWAVBlob,
success: function(response) {
console.log("success");
}
});
}
Controller:
class WormController < ApplicationController
def create
end
def
save
audio = params[:someData]
p audio
save_path = Rails.root.join("public/audioFiles")
# Open and write the file to file system.
File.open(save_path, 'wb') do |f|
f.write audio.read
end
render :text=> 'hi'
end
end
That code was based on this post: Save audio file in rails.
But although I am posting something, it seems to be a string instead of the actual bytes of the audio file. This is the rails console error I receive:
NoMethodError (undefined method `read' for "[object Blob]":String):
Any ideas? I feel like I must be missing some encoding step, or maybe I'm just completely misunderstanding what a blob is. Why can't I just post it directly like this?

You need to convert blob to base64url and send it in data by ajax to upload in rails it is working for me
var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
var base64data = reader.result;
var savedWAVBlob=base64data
console.log(savedWAVBlob );
}
also in controller
require 'base64'
save_path = Rails.root.join("public/audio")
unless File.exists?(save_path)
Dir::mkdir(Rails.root.join("public/audio"))
end
data=params[:url]
audio_data=Base64.decode64(data['data:audio/ogg;base64,'.length .. -1])
File.open(save_path+"_audio", 'wb') do |f| f.write audio_data end
current_user.user_detail.audio=File.open(save_path+"_audio")
current_user.user_detail.audio_content_type="application/octet-stream"

Related

How to upload an image in img tag to a different server as a file or formData but not base64

Basically, there are a lot of images on the website. The user selects one image by clicking on it and it's uploaded to a different server.
I have an implementation which reads the user selected image src in img tag and uploads it to a different server as a base64 string attached to the end of URL via ajax.
I want this image to be uploaded as formData or file upload. Any help?
My current implementation creates formData using the output of file reader and uploads it as data in ajax:
var img_data = new FormData();
// img is output of file reader which looks like "data:image/jpg;base64,XXX"
img_data.append("image", img, "site_image.jpg");
$.ajax({
type : 'post',
url : "other server url",
data : img_data,
contentType: "image/jpeg",
processData: false,
...}),
But I couldn't see any useful data in my flask server. Here's output in flask:
files: len: 0 type: <class 'werkzeug.datastructures.ImmutableMultiDict'>
[]
data: len: 136 type: <type 'str'>
------WebKitFormBoundaryJ8YC9pZxjqRl0tyD
Content-Disposition: form-data; name="image"
------WebKitFormBoundaryJ8YC9pZxjqRl0tyD--
form: len: 0 type: <class 'werkzeug.datastructures.ImmutableMultiDict'>
ImmutableMultiDict([])
Got weird stuff in data str but couldn't get any image data.
Uploading images without converting to base64 required you to use FormData.
For me I use jQuery Form, It'll mimic ajax post for you and (do fallback if
method not supported in browser).
If you want to do it manually, see this question and its best answer:
How to use FormData for ajax file upload

How to download a file through a custom POST request with CasperJS

I am writing a crawler and needs to download file generated after a form request using POST.
I have successfully used this.download(url,'POST',Params) for regular forms.
One of the sites has many fields using the same name, thus preventing me from using the regular download method.
After trying a lot of things, I tried with $.ajax() and __utils.sendAJAX() to process the form like this:
response = this.evaluate(function(){
url=...
params = $('form#theirForm').serialize();
data = __utils__.sendAJAX(url, 'POST', params,false,{contentType:"application/x-www-form-urlencoded"});
return __utils__.encode(data);
});
function decode_base64(s) { var e={},i,k,v=[],r='',w=String.fromCharCode; var n=[[65,91],[97,123],[48,58],[43,44],[47,48]]; for(z in n){for(i=n[z][0];i<n[z][1];i++){v.push(w(i));}} for(i=0;i<64;i++){e[v[i]]=i;} for(i=0;i<s.length;i+=72){ var b=0,c,x,l=0,o=s.substring(i,i+72); for(x=0;x<o.length;x++){ c=e[o.charAt(x)];b=(b<<6)+c;l+=6; while(l>=8){r+=w((b>>>(l-=8))%256);} } } return r; }
casper.then(function() {
utils.dump(response);
fs.write("test.zip",decode_base64(response),'w');
});
The codes returns me base64 data which I convert and store in a test.zip file.
But I juste can't uncompress it, says it is corrupted.
I dump the data of a correct zip file =>
PK^C^D^T^#^H^#^H^#<F4><89><96>F^#^#^#^#^#^#^#^#^#^#^#^#?^#^#^#fourniture denr<E9>es alimentaires - dietetique infantile\CCAP.pdf<AC><BC>^ET\K<D3><F7>;^D<B7><U+0B81>^#<C1><99>^Y^F'^D<B7><E0>^D^ON<90><E0><EE><EE><EE>^Dwww'^P<9C>^D^H<EE>^^܂<C3>%'<CF>9<E7><C9><F7><U+07B5><BE>7<F7>f^SVOzf
Compared it with the first line of my file :
PK^C^D^T^#^H^#^H^#)_^M^#^#^#^#^#^#^#^#^#^#^#^#^#b^#^#^#fourniture denr<FD>es alimentaires - dietetique infantile\Bordereau de prix dietetique infantile.xlsx<FD>zuT<FD>I<FD><FD><FD><FD>^^4hp^M^D^M^R^H<FD>.<FD><FD>}p<FD>3<FD>kpw<FD>#pw^M<FD><FD>^R4<FD>Gv<FD>~<FD>[<FD><FD><FD><FD><FD>
Anyone has an idea of what could have gone wrong?
I have tried so many things (encoding tools, encoding settings, dumping from the chrome console to get pure base64, etc.)
I don't understand why it is related to latin-1 or utf8 encoding, since a website asks me to select which encoding to use. Tried both.
casper.download() happily accepts a serialized form instead of an object, so you can still use it. You just have to serialize the form in the page context beforehand:
var formData = casper.evaluate(function(){
return $('form#theirForm').serialize();
});
var url;
casper.download(url, targetFile, 'POST', params);
The only problem might be, that another mimeType is used: "text/plain; charset=x-user-defined".
In that case, you will have to recreate the whole cascade of functions that go into casper.download():
var url;
var response = casper.evaluate(function(url){
var params = $('form#theirForm').serialize();
var data = __utils__.sendAJAX(url, 'POST', params, false);
return __utils__.encode(data);
}, url);
var cu = require('clientutils');
fs.write("test.zip", cu.decode(response), 'wb');
"application/x-www-form-urlencoded" is used by default for __utils__.sendAJAX().

Ajax Post dataURL using multipart/form-data

I am am writing a program that pulls a dataURL from a canvas element and sends it server side to be converted back to a jpg and saved. What I need to do now, is to programmatically get this image from the server and post it using another ajax function as a multipart/form-data encoded form. There is some code that cannot be modified and it is expecting this type of post.
To be specific: how do I take an image from the server and put it into a POST of enctype="multipart/form-data" such that the code receiving the request sees it as if it were an post coming from a regular form. All of this using Jquery or JavaScript Ajax.
Extra info: The code being posted to is in ASP classic. I am using PHP to convert the DataURL.
PHP Code:
//Get the base-64 string from data
$data=$_POST['img_val'];
list($type, $data) = explode(';', $data);
list(, $data) = explode(',', $data);
$data = base64_decode($data);
file_put_contents('filename.png', $data);
$images_orig = imagecreatefrompng('filename.png');
$photoX = ImagesX($images_orig);
$photoY = ImagesY($images_orig);
$images_fin = ImageCreateTrueColor(650, 650);
ImageCopyResampled($images_fin, $images_orig, 0, 0, 0, 0, 650+1, 650+1, $photoX, $photoY);
imagejpeg($images_fin, NULL, 100);
ImageDestroy($images_orig);
ImageDestroy($images_fin);
The above code can be use to return either a binary Image File or to save the contents to disk and return an image URL. I'm not sure which will be more useful to my end goal.
Thanks to All.
So for those who need a solution to something like this, the answer is a bit complicated. The above PHP code does properly convert the image from a dataURL to a png, and then resizes it as a jpg. The proper way to make a multipart/form-data Post from ajax goes like this:
var url = "example.php"
var image_as_blob = previousAjaxRequest(); //use xhr.responseType = "blob" and a GET request to grab server image
var form = new FormData();
form.append('image_variable_name', image_as_blob, 'file_name.extension'); //the third argument seemed to make all the difference in server code seeing the file correctly.
$.ajax({
url: url,
type: 'post',
data: form,
cache: false,
contentType: false, //required for multipart
processData: false //required for multipart
}).done(function( data ) {
//do what you want with returned data
});

Sending Data from Python (Flask) to Javascript?

I am trying to implement a drag and drop file uploader on my website. Files are uploaded immediately after they are dropped, and I would like to generate a URL with flask that will pop up under the previews. I am using dropzone.js. In the documentation for dropzone a sample is provided as a guide for sending data back from the server to be displayed after a file uploads. https://github.com/enyo/dropzone/wiki/FAQ#i-want-to-display-additional-information-after-a-file-uploaded
However, when I try to use url_for in the inline Javascript in my Jinja template that creates the dropzone, I am getting back a link that looks like /%7Bfilename%7D
Just to be sure I popped a quick print statement in there for the URL, and it comes out fine in the console.
My uploader in python:
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if is_image(file.filename): # generates a shortened UUID name for the image
filename = shortuuid.uuid()[:7] + "." + file.filename.rsplit(".", 1)[1]
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
#app.route ('/<filename>')
def uploaded_image(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
and the inline JS in my index.html template:
<script>
var mydropzone = new Dropzone(document.body, {
url: "{{url_for('upload_file')}}",
previewsContainer: "#previews",
clickable: "#clickable",
init: function() {
this.on("success", function(file, responseText) {
var responseText = " {{ url_for('uploaded_image', filename='{filename}')}} ";
var span = document.createElement('span');
span.setAttribute("style", "position: absolute; bottom: -50px; left: 3px; height: 28px; line-height: 28px; ")
span.innerHTML = responseText;
file.previewTemplate.appendChild(span);
});
}
});
Am I missing something fundamental here? Do I need to use something like JSON/Ajax (never worked with these but Googling always brought them up), because the URL is data send back from the server?
Simply:
Return the file path to the client:
def upload_file():
if request.method == 'POST':
# ... snip ...
return url_for('uploaded_image', filename=filename)
Remove the var responseText line in your on('success') function, since you will be getting the URL back in the response.
You are very close to getting it, but you will have to send the URL over JSON.
The issue is that you call url_for('uploaded_image') when the page first loads (in the Jinja template), before the URL is actually available. It is thinking you are asking for the url for a file called {filename}.
Try returning a JSON response from your POST request which has the new URL:
return jsonify({'fileURL':url_for('uploaded_image', filename=filename)})
From there, you can do whatever you would like with JS. What you have should work, just get the URL from responseText.
EDIT: Fixed return.

Opening a JSON file from javascript

I have a C# functoin in my MVC application that returns a JSON representation of a csv file. I am trying to catch that in javascript and open the file. Basically I want the browser do its thing and let the user decide if he want to open it, save it or cancel. I am unable to get that popup to ask me to open the file. Below are the functions
C# Function
[HttpPost]
public ActionResult ExportToCsv(string fileContents, string fileName)
{
fileContents = fileContents.Replace("-CARRIAGE-", "\r\n");
return Json(new { url = File(Encoding.UTF8.GetBytes(fileContents), "text/csv", fileName) }); ;
}
This is the javascript where I am making the ajax call to the function
$("#btnExport").click(function (event) {
event.preventDefault();
var csv = table2csv(noteTypeTable, "full", "Table.dataTable", "noteTypes");
$.ajax({
url: "/Admin/Admin/ExportToCsv/?fileContents=" + csv + "&fileName=NoteTypes.csv",
type: 'Post',
success: function (result) {
window.open(result.url);
}
});
});
I know I am missing something. Can someone please help.
EDIT
After reading through all the potential answers and comments, this is what I am trying to achieve. So if my code is all horribly wrong please let me know.
I have a grid and I have an export to excel button. I have a method that converts the data i want into comma delimited text in javascript itself. I need to present this to the user as a downloadable csv file. For this I was creating the File object using the controller method. The previous incarnation was a Get method and I faced limitations due to querystring length restrictions. So I tried converting it to a POST method and it is not working.
This is the previous version of the code that works for smaller amounts of data
Javascript
$("#btnExport").click(function (event) {
event.preventDefault();
var csv = table2csv(noteTypeTable, "full", "Table.dataTable", "noteTypes");
window.location.href = "/Admin/Admin/ExportToCsv/?fileContents=" + csv + "&fileName=NoteTypes.csv";
});
C# Function
[HttpGet]
public ActionResult ExportToCsv(string fileContents, string fileName)
{
fileContents = fileContents.Replace("-CARRIAGE-", "\r\n");
return File(Encoding.UTF8.GetBytes(fileContents), "text/csv", fileName);
}
Hope this now gives you all more context. Basically I needed to convert my GET method to a POST method and use it from Javascript.
If you use ajax, you're expected to handle the result in code. But you can't trigger a file download (directly) that way.
Instead, create a (hidden) form and post it to a (hidden) iframe (by giving the iframe a name and specifying that as the target of the form), making sure that the response specifies the header Content-Disposition: attachment. That will trigger the browser to offer to save the file. Optionally in the header you can suggest a filename for the file by adding ; filename="fname.ext" to the header value. E.g. Content-Disposition: attachment; filename="fname.ext".
The client-side looks something like this:
$("#btnExport").click(function (event) {
event.preventDefault();
var csv = table2csv(noteTypeTable, "full", "Table.dataTable", "noteTypes");
var frame = $('iframe[name="formreceiver"]');
if (!frame.length) {
frame = $('<iframe name="formreceiver"></iframe>').appendTo(document.body).css("display", "none");
}
var form = $("#senderform");
if (!form.length) {
form = $('<form id="senderform"></form>').appendTo(document.body);
}
form = form[0]; // Raw DOM element rather than jQuery wrapper
form.target = "formreceiver";
form.action = "/Admin/Admin/ExportToCsv/?fileContents=" + csv + "&fileName=NoteTypes.csv";
form.method = "POST";
form.submit();
});
The server side is just a standard form response with the Content-Disposition header.
I've used this technique for years, with browsers from IE6 onward. My usual setup already has the iframe in the markup (rather than creating it as above), but other than that this is basically what I do.
Note that it's important you're doing this in response to a button click by the user. If you weren't, there would be popup-blocker issues.
You can't save binary files using ajax - it's restricted due to security reasons. If you'd like the user to save the file - return a binary stream from your controller as you post the data back in JSON format (ie if the user wants to save it).
I've done a similar thing here: How to properly create and download a dynamically generated binary *.xlsx file on .net MVC4?
Maybe save it as a json file instead and load in the current page's DOM. Although I though I am confused, don't you just have a JSON response containing a URL to your CSV file is which is just that a CSV file, not a JSON representation of CSV?
$("#btnExport").click(function (event) {
event.preventDefault();
var csv = table2csv(noteTypeTable, "full", "Table.dataTable", "noteTypes");
$.ajax({
url: "/Admin/Admin/ExportToCsv/?fileContents=" + csv + "&fileName=NoteTypes.json",
type: 'Post',
success: function (result) {
var myDummyElement = $('<div>'); //dummy div so you can call load
myDummyElement .load('result.url #myJson');
}
});
});
<div id="myJson"></div>
[HttpPost]
public ActionResult ExportToCsv(string fileContents, string fileName)
{
//change fileName to be a .json extension
fileContents = fileContents.Replace("-CARRIAGE-", "\r\n");
return Json(new { url = File(Encoding.UTF8.GetBytes(fileContents), "text/csv", fileName) }); ;
}

Categories

Resources