I would like to upload multiple image files in one request using multipart. I have reviewed the Karate examples on this, but the multiple file upload does not meet my need (/multiple endpoint here - https://github.com/intuit/karate/blob/master/karate-demo/src/main/java/com/intuit/karate/demo/controller/UploadController.java). My service method (Spring REST) signature expects an array of MultipartFile[] so that I can accept any number of files. Here is my scenario:
Scenario: Upload multiple files
* def json = {}
* set json.files[0] = { read: 'file1.jpg', filename: 'file1.jpg', contentType: 'image/jpeg' }
* set json.files[1] = { read: 'file2.jpg', filename: 'file2.jpg', contentType: 'image/jpeg' }
Given path '/rest'
And multipart files json
When method post
Then status 200
And here is the Spring web service method (just trying to receive the files right now, so the method doesn't do much):
#PostMapping("/rest")
public String handleFileUpload(#RequestParam("file") MultipartFile[] file) {
System.out.println("Len: " + file.length);
for(MultipartFile currentFile : file) {
System.out.println("In here: " + currentFile.getOriginalFilename());
}
return file[0].getOriginalFilename();
}
When I run this I receive a Karate error: 'multipart file value should be json'
If I change the scenario to do this:
Scenario: Upload multiple files
* def json = {}
* set json.files = { read: 'file1.jpg', filename: 'file1.jpg', contentType: 'image/jpeg' }, { read: 'file2.jpg', filename: 'file2.jpg', contentType: 'image/jpeg' }
Given path '/rest'
And multipart files json
When method post
Then status 200
Then the test executes ok, but only one file ends up in the MultipartFile array 'files' (service method argument).
What is the proper way to upload multiple files to the web service method above using Karate?
Edit: Adding client code (below) and updated Spring method above.
Here is a simple HTML form that will submit multiple files to the Spring method above:
<form method="POST" enctype="multipart/form-data" action="/rest">
<table>
<tr><td>File to upload:</td><td><input type="file" name="file" /></td></tr>
<tr><td>File to upload:</td><td><input type="file" name="file" /></td></tr>
<tr><td></td><td><input type="submit" value="Upload" /></td></tr>
</table>
</form>
When submitted I get 2 files in the service method.
Seems that it does work, you just need to send the same parameter twice in the feature file
And multipart file files = { read: 'mergeTest.pdf', filename: 'upload-name.pdf', contentType: 'application/pdf' }
And multipart file files = { read: 'mergeTest.pdf', filename: 'upload-name.pdf', contentType: 'application/pdf' }
And multipart field filesMetadata = .........................................
my service:
public ResponseEntity<StreamingResponseBody> mergePdfs(#RequestPart #Validated
List<FileMetaData> filesMetadata, #RequestPart #Validated List<MultipartFile> files)
Wow, never seen this before and it is likely that Karate does not support it. I'm also wondering if this is legal as per the HTTP spec - as far as I know - each file has to have unique field-name. Do you have the corresponding client-side code for the Apache HTTP Client, that would help.
EDIT: also see answer by Esteban Lopez below: https://stackoverflow.com/a/59833358/143475
Your best bet may be to submit a feature request and also contribute code to expedite. Note that this is the first time in 2 years that anyone has reported this as a problem.
Related
I am trying to migrate one of api test to karate framework. However I am unable to write the corrent step defined in karate documentation. Maybe I am missing some basic syntax..but could anyone have any idea how we write following steps in karate feature
requestPostDoc.header("x-api-key","FG6dcYHN9N7PYKfWCUlGo5QGTwZhv2Re1MrDSOTV");//New chnages
requestPostDoc.contentType("multipart/form-data").multiPart("part2-file",file).formParam("part1-json",objDocumentWrite.toJSONString());
requestPostDoc.queryParam("loadProperties",true); //New changes
responseForNewCaseDocFile=requestPostDoc.post("https://vrh0oox3hl.execute-api.eu-central-1.amazonaws.com/default/");//New changes
filterableRequestSpecification = (FilterableRequestSpecification) requestPostDoc;
filterableRequestSpecification.removeQueryParam("loadProperties");
I have written following feature file in karate:
Given url 'https://vrh0oox3hl.execute-api.eu-central-1.amazonaws.com/default/'
And header x-api-key = 'FG6dcYHN9N7PYKfWCUlGo5QGTwZhv2Re1MrDSOTV'
And header Authorization = 'Bearer ' + jwt
And param loadProperties = true
And multipart file info = { read: 'classpath:testData/documentWrite.json', filename: 'documentWrite.json' }
And multipart file Uploading = { read: 'classpath:testData/TextFile.txt', filename: 'TextFile.txt' }
When method post
Then print response
Then status 200
When I execute this test i am getting 400 response code
status code was: 400, expected: 200, response time in milliseconds: 252, url: https://vrh0oox3hl.execute-api.eu-central-1.amazonaws.com/default/?loadProperties=true, response:
Based on the cURL command in the comments, this is my best guess. The rest is up to your research. Read the docs and tweak the Content-Type and other sub-headers if needed. You need to figure this out depending on what your server wants: https://github.com/karatelabs/karate#multipart-file
* multipart file part1-json = { read: 'documentWrite.json' }
* multipart file part2-file = { read: 'TextFile.txt' }
For anyone coming across this question in the future and if you are stuck, get a friend if needed and go through this exercise together: https://github.com/karatelabs/karate/issues/1645#issuecomment-862502881
This stuff can be hard and needs time. There are no short cuts.
I am trying to pass the absolute value of a file to the read function for classpath.
If I pass absolute path along with classpath it works fine. But when I pass embedded expression its not working
My code is as below
Scenario: create swagger first RAD
Given url appServer
Given param creationMethod = 'SWAGGER_FIRST'
And path '/integration/rest/rad'
And header X-CSRF-TOKEN = csrfToken
* cookie JSESSIONID = jsessionid
* cookie route = routevalue
* configure charset = null
print swaggerDetailsinputFile
print swaggerInputJsonFile
Given multipart file inputData = { read: 'classpath: #(swaggerDetailsinputFile)', filename: 'blob', contentType: 'application/json' }
Given multipart file swaggerFile = { read: 'classpath:ic/feature/RAD/swagger.json', filename: 'blob', contentType: 'application/json' }
And header Content-Type = 'multipart/form-data'
When method post
Need a way to pass embedded expression to classpath value for read function
Try this:
read: '#("classpath:" + swaggerDetailsinputFile)'
Make sure you read this part of the docs: https://github.com/intuit/karate#rules-for-embedded-expressions
I have been trying to implement file upload to BrickFTP using Dropzone, but the uploaded file won't open because it contains the WebKitFormBoundary at the top of the file content.
I'm putting method as PUT in Dropzone configuration as per BrickFTP's documentation. BrickFTP uses Amazon S3 so the files are actually being uploaded to S3. I did everything as per their documentation, and everything worked except this last problem I'm having with those extra information at the top of the uploaded file content.
Here is the Coffeescript code responsible for file upload:
brickFTPData = {}
# As per BrickFTP docs, step 1 is
# to get the dedicated upload url for each file by sending a
# request to REST API to indicate intent to upload a file
getUploadUri = (file) ->
result = ""
$.ajax
url : '/a/portal-dev/get-api-keys'
type : 'POST'
dataType : 'json'
data : { file_name: file.name }
async : false
success : (data, textStatus, jqXHR) ->
brickFTPData[file.upload.uuid] =
file_name : file.name
upload_uri : data.upload_uri
ref : data.ref
result = data.upload_uri
return result
# 3rd step is to notify the REST API that the file upload is complete.
finishUpload = (file) ->
$.ajax
url : '/a/portal-dev/upload-done'
type : 'POST'
dataType : 'json'
data :
ref : brickFTPData[file.upload.uuid].ref
file_name : brickFTPData[file.upload.uuid].file_name
success : (data) ->
delete brickFTPData[file.upload.uuid]
console.log data.status
# 2nd step is to upload the file
sampleQuoteDropzone = new Dropzone "#sampleQuoteDropzone",
url : '/demo'
method : 'PUT'
headers : {"Content-Type": "application/octet-stream"}
success : (file, request) ->
finishUpload(file)
sampleQuoteDropzone.on 'processing', (file) ->
upload_uri = getUploadUri(file)
sampleQuoteDropzone.options.url = upload_uri
Upload works fine using the above code but as said when I open the uploaded file into a text editor it starts with following code:
------WebKitFormBoundaryw4bIakMBbkp7ES2E
Content-Disposition: form-data; name="file"; filename="IMG_5652.jpg"
Content-Type: image/jpeg
If I delete these lines and save the file it will work.
But this problem is not seen when uploading file using a regular ajax call outside Dropzone. Following is the working code:
$.ajax
url: data.upload_uri
type: 'PUT'
contentType: 'application/octet-stream'
data: file
processData: false
success: (response) ->
finishUpload(file, data)
Can anyone please advise how to solve this problem?
I solved the problem with following code:
sampleQuoteDropzone.on 'sending', (data, xhr, formData) ->
_send = xhr.send
xhr.send = ->
_send.call(xhr, data)
This solution was actually found here. I saw this before posting this question here, but wasn't sure if this is the right problem/solution. I contacted BrickFTP and they responded fairly quickly saying this solution may work for me, and yes it certainly does.
I would like to upload a xml file from the local workspace and use it as model. I can upload files to the server but i don't know how to retrieve the data from the uploaded object and set or load it to my model
Here is how I m uploading the file.
jQuery.ajax({
url: oFileUploader.getUploadUrl(),
headers: oHeaders,
type: 'PUT',
cache: false,
contentType: false,
processData: false,
data: file
});
}
I could not use the standard oFileUploader.upload() because it uses the http method "POST" and I wanted to do a "PUT"
Thank you
It is not clear how you get the contents of the file variable that you are passing to the jQuery.ajax call. Depending on this, you have three options hat come to mind:
Directly use the file variable contents in a model.
Use the FileReader API to read the file contents and put them in a model.
Make the back-end service return the XML content of the file as a response to the AJAX call and then store the response in a model.
I've a sails application. So in the backend I've node js server and in the front end I've an angular js application.
And from the frontend I want to upload some file to s3. So for that I'm using s3-skipper in the backend.
So in the frontend I've
<form id="formUpload"
method = "post"
action = "/bin/upload"
enctype= "multipart/form-data" >
<input type="file" id="inFile" name="file" />
<input type="button" value="Upload" onclick="uploadFile();" />
</form>
And then to upload the file in s3 using skipper I'm accessing the file stream in the backend as:
req.file("file");
And this whole process works perfectly.
Now I've another scenario when I'm recording an audio file from the browser and I want to upload the file using same s3-skipper to s3.
But in this case I don't have any html form element and input type file for obvious reason. And for the audio file I've base64 audio dataUrl:
data:audio/ogg;base64,T2dnUwACAAAAAAAAAABx+oohAAAAAH....
And as I can't use form submit I want to send the data using $http. So what I'm doing:-
var blob = $scope.dataURItoBlob(audioDataUrl, 'audio/ogg');
var formData = new FormData();
formData.append('file', blob);
$http({
method: 'POST',
url: '/file/upload/multi',
headers: { 'Content-Type': 'multipart/form-data;' },
data: formData
And after sending the request I can see some data being sent in the browser network.
But in the backend this is what I'm getting as req.file("file"):
{ _fatalErrors: [],
isNoop: true,
_files: [],....
So, can anyone help me here how to upload the file properly here?