Getting actual files to send to backend from dropzone - ruby-on-rails-4

I am having difficulties grabbing files from Dropzone object (using Vue-Dropzone) and appending them to a custom formData object I am building with other params too.
What I am trying to achive is a form with a Dropzone in it which submits via ajax and I am trying to grab all files the user selected and create an object to pass to backend in the form of
files[file_1] = file_1
files[file_2] = file_2
and so on. I have used the below code but no success
let files = {};
_.each(this.$refs.dropzoneID.getQueuedFiles(), (file, index) => {
files[index] = file;
});
// console.log(files);
this.formData.append('files', files);
process_form_via_axios_call
What i get in the backend is:
params={"files"=>"[object Object]"}
While I expect something like this:
{"files" => {"file_1"=>#<ActionDispatch::Http::UploadedFile:0x007fd14c9ec940 #tempfile=#<Tempfile:/var/folders/lk/bhps4r5d3s1fzmywxlp59f480000gn/T/RackMultipart20171002-87936-1tnd839.jpg>, #original_filename="restaurant-person-woman-coffee-medium.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"file\"; filename=\"restaurant-person-woman-coffee-medium.jpg\"\r\nContent-Type: image/jpeg\r\n">......}
How can i achieve this?

Actually was able to resolve this by doing this:
_.each(this.$refs.dropzoneID.getQueuedFiles(), (file, index) => {
this.formData.append('files[file_' + index +']', file);
});
Worked like a charm.

Related

Remove uploaded files with filepond

I really enjoy the filepond library and would like to implement it in my flask app. Since I was not able to find any useful examples online, I started to write my own, small, proof of concept web application. I would like to upload multiple images to the server and save the filenames in the database. Furthermore, I would like to edit an entry and add additional files or remove the existing ones.
So far I figured out how to upload and revert files before the form is submitted. I am also able to load existing files inside the edit form. Just when I click the 'x' button on a loaded image inside the edit form the image is removed from the filepond window and a 'removefile' event is fired, but the file still remains on the server. Is it possible to trigger the revert request on a loaded file or is there a better solution altogether?
x-button does not remove the file from the server
Here are the relevant snippets from my js file:
FilePond.registerPlugin(
FilePondPluginFileValidateSize,
FilePondPluginImagePreview,
FilePondPluginFileRename,
FilePondPluginFileValidateType
);
inputElement = document.querySelector(".filepond");
token = document
.querySelector('input[name="csrf_token"]')
.getAttribute("value");
FilePond.setOptions({
server: {
headers: { "X-CSRF-TOKEN": token },
process: "./process",
revert: "./revert",
load: {
url: "../",
}
},
});
const filepond = FilePond.create(inputElement, {
// Here I pass the files to my edit form in the following format:
//
// files: [
// {
// source: 'static/images/some_name.png',
// options: {
// type: 'local'
// }
// }]
});
The relevant code from .py file:
#app.route("/process", methods=["POST"])
#app.route("/edit/process", methods=["POST"])
def process():
upload_dir = "static/images"
file_names = []
for key in request.files:
file = request.files[key]
picture_fn = file.filename
file_names.append(picture_fn)
picture_path = os.path.join(upload_dir, picture_fn)
try:
file.save(picture_path)
except:
print("save fail: " + picture_path)
return json.dumps({"filename": [f for f in file_names]})
#app.route("/revert", methods=["DELETE"])
#app.route("/edit/revert", methods=["DELETE"])
def revert():
upload_dir = "static/images"
parsed = json.loads(request.data)
picture_fn = parsed["filename"][0]
picture_path = os.path.join(upload_dir, picture_fn)
try:
os.remove(picture_path)
except:
print("delete fail: " + picture_path)
return json.dumps({"filename": picture_fn})
Here is the repository to my full flask-filepond app:
https://github.com/AugeJevgenij/flask_filepond
Please excuse me if the question is unclear, does not make sense or the code is written poorly.
I just started programming a few months ago.
Acording to filepond documentation you can remove a file stored locally on the server like this:
FilePond.setOptions({
server: {
remove: (source, load, error) {
// 'source' is the path of the file and should be sent to a server endpoint via http
// call the load method before ending the function
load()
}
}
})
then on your server where you receive the source (path), use it to delete the file. Keep in mind that this is a risky approach to get your website hacked!

How to build a Postman url query with Pre-request Scripts

I'm trying to use a pre-request script to build out a request object based on data pulled from a CSV file. The problem is that the request seems to be set in stone prior to the pre-request script being run. That would seem to make this a mid-request script almost rather than a pre-request.
My code is as follows:
if(ipList === undefined) ipList = "1.2.3.4,2.3.4.5,123.234.345.465";
let ips = ipList.split(',');
let queryArray = [];
for( i=0; i<ips.length; i++){
queryArray.push({ "key": "ip", "value": ips[i] });
}
console.log(queryArray);
pm.request.url.query = queryArray;
console.log(pm.request);
When I hardcode a url query variable in the request to equal 4.3.2.1, the pm.response.url object like this:
pm.request.url.query[0] = {key:"ip", value:"4.3.2.1"}
Note that the url.query[0] part of the object matches the parameter in the actual get request.
When I change the value of pm.request.url.query to equal the new query array, however as you can see here, the query array is set correctly, but the parameters are not appended to the request URL.
So unless I'm doing something wrong, it appears that the request is immutable even to the pre-request scripts.
So my question is this:
Is there a way to modify the url params of a request prior to making the request?
BTW: I know that is might seem odd to have multiple params with the same key in a query, but that's the way this API works and hard coding multiple ip addresses in the query works just fine.
You could just assign a new value to pm.request.url.
Here I had some query params already in the URL, which I had to edit:
const urlSplit = request.url.split('?');
const paramsString = urlSplit[1]; // the second part actually represents the query string we need to modify
const eachParamArray = paramsString.split('&');
let params = {};
eachParamArray.forEach((param) => {
const key = param.split('=')[0];
const value = param.split('=')[1];
Object.assign(params, {[key]: value});
});
params.bla = params.bla + 'foobar';
newQueryString = Object.keys(params).map(key => key + '=' + params[key]).join('&');
pm.request.url = urlSplit[0] + '?' + newQueryString;
In the end, I just constructed a new URL, using the first part of the previous one & the query string with the edited bla parameter.
This seemed to work for me--it didn't change what the UI shows the query string is, but it changed what the actual request was (looking at the console log)
pm.request.url.addQueryParams(["a=1", "b=2"])
pm.request.url.query.remove("b")
I have some parameters called "script_loginAs" etc... named such that people on my team know the parameter is evaluated and not sent.

Postman - How to store multiple values from a response header in a var or just be able to see them

Using a GET in postman with the URL posted below, I am able to store the entire response header in question with all of its data in a var, the issue for me is how do I verify the pieces of data inside that var
here is my URL
http://localhost/v1/accounts?pageNumber=1&pageSize=2
[
using postman I am able to get the above in a var
var XPaginationData = postman.getResponseHeader(pm.globals.get("PaginationHeader"));
pm.globals.set("XPaginationData", XPaginationData);
is there a way to get the individual values inside the response header X-Pagination stored in a different var to assert later
using this in postman
pm.globals.set("XPaginationData", JSON.stringify(pm.response.headers));
console.log(JSON.parse(pm.globals.get('XPaginationData')));
console.log(JSON.parse(pm.globals.get('XPaginationData'))[4].value);
I get
how would i go about getting "TotalCount" for example
BIG EDIT:
thanks to a coworker, the solution is this
//Filtering Response Headers to get PaginationHeader
var filteredHeaders = pm.response.headers.all()
.filter(headerObj => {
return headerObj.key == pm.globals.get("PaginationHeader");
});
// JSON parse the string of the requested response header
// from var filteredHeaders
var paginationObj = filteredHeaders[0].value;
paginationObj = JSON.parse(paginationObj);
//Stores global variable for nextpageURL
var nextPageURL = paginationObj.NextPageLink;
postman.setGlobalVariable("nextPageURL", nextPageURL);
You could use JSON.stringfy() when saving the environment variable and then use JSON.parse() to access the different properties or property that you need.
If you set a global variable for the response headers like this:
pm.globals.set('PaginationHeader', JSON.stringify(pm.response.headers))
Then you can get any of the data from the variable like this:
console.log(JSON.parse(pm.globals.get('PaginationHeader'))[1].value)
The image shows how this works in Postman. The ordering of the headers returned in the console is inconsistent so you will need to find the correct one to extract data from the X-Pagination header
Looks like an issue with Postman itself.
The only solution that worked for me was to stringify & parse the JSON again, like this:
var response = JSON.parse(JSON.stringify(res))
After doing this, the headers and all other properties are accessible as expected.

Can I convert browser generated image blob to image file for upload?

I'm using fabric.js to dynamically create textures in Threes.js, and I need to save the textures to AWS. I'm using meteor-slingshot, which normally takes images passed in through a file selector input. Here's the uploader:
var uploader = new Slingshot.Upload("myFileUploads");
uploader.send(document.getElementById('input').files[0], function (error, downloadUrl) {
if (error) {
console.error('Error uploading', uploader.xhr.response);
alert (error);
}
else {
Meteor.users.update(Meteor.userId(), {$push: {"profile.files":downloadUrl}});
}
});
Uploading works fine from the drive ... but I'm generating my files in the browser, not getting them from the drive. Instead, they are generated from a canvas element with the following method:
generateTex: function(){
var canvTex = document.getElementById('texture-generator');
var canvImg = canvTex.toDataURL('image/jpeg');
var imageNew = document.createElement( 'img' );
imageNew.src = canvImg;
}
This works great as well. If I console.log the imageNew, I get my lovely image with base 64 encoding:
<img src=​"data:​image/​jpeg;​base64,/​9j/​
4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgID
//....carries on to 15k or so characters
If I console.log a file object added from the drive via filepicker ( not generated from a canvas ), I can see what the file object should look like:
file{
lastModified: 1384216556000
lastModifiedDate: Mon Nov 11 2013 16:35:56 GMT-0800 (PST)
name: "filename.png"
size: 3034
type: "image/png"
webkitRelativePath: ""
__proto__: File
}
But I can't create a file from the blob for upload, because there is no place in the file object to add the actual data.
To sum up I can:
Generate an image blob and display it in a dom element
Upload files from the drive using meteor-slingshot
inspect the existing file object
But I don't know how to convert the blob into a named file, so I can pass it to the uploader.
I don't want to download the image, (there are answers for that), I want to upload it. There is a "chrome only" way to do this with the filesystem API but I need something cross browser (and eventually cross platform). If someone could help me with this, I would have uncontainable joy.
Slingshot supports blobs just as well as files: https://github.com/CulturalMe/meteor-slingshot/issues/22
So when you have a canvas object called canvTex and a Slingshot.Upload instance called uploader, then uploading the canvas image is as easy as:
canvTex.toBlob(function (blob) {
uploader.send(blob, function (error, downloadUrl) {
//...
});
});
Because blobs have no names, you must take that into account when defining your directive. Do not attempt to generate a key based on the name of the file.

Multi-part form data upload with Akka HTTP

I'm trying to figure out how to create a multi-part form data request
with Akka HTTP (client API) but I can't find a way to express form data.
Does anyone know how to create form data that would take a file or input stream?
I guess a bit late right now, but this example, has both a client and a server.
I copy the relevant part
def createEntity(file: File): Future[RequestEntity] = {
require(file.exists())
val formData =
Multipart.FormData(
Source.single(
Multipart.FormData.BodyPart(
"test",
HttpEntity(MediaTypes.`application/octet-stream`, file.length(), SynchronousFileSource(file, chunkSize = 100000)), // the chunk size here is currently critical for performance
Map("filename" -> file.getName))))
Marshal(formData).to[RequestEntity]
}
def createRequest(target: Uri, file: File): Future[HttpRequest] =
for {
e ← createEntity(file)
} yield HttpRequest(HttpMethods.POST, uri = target, entity = e)
Simplest way to achieve this would be:
val formData = Multipart.FormData.
fromFile("<FORM_DATA_KEY>",
MediaTypes.`application/octet-stream`,
file = file,
100000)
val httpRequest = HttpRequest(HttpMethods.POST, uri = target, entity = formData.toEntity)
On the first line you can also use Multipart.FormData.fromPath which will accept file path instead of file object itself.