This question already has answers here:
Download a file from Servlet using Ajax
(3 answers)
Closed 5 years ago.
I am trying to download a file using ajax and a servlet, but the maximum I get is that in succes I get the file parseado
I have this section of the servlet:
else if(type.equals("downloadDocument")){
String file = request.getParameter("filePath");
File f = new File(file);
if (f.exists() && f.isFile()){
OutputStream out = response.getOutputStream();
FileInputStream in = new FileInputStream(f);
byte[] buffer = new byte[4096];
int length;
while ((length = in.read(buffer)) > -1){
out.write(buffer, 0, length);
}
in.close();
out.flush();
}
And that call:
$.ajax({
type : "POST",
url : "./ServletDocuWindow?downloadDocument",
data : datos,
success : function(r) {
}
});
Instead of downloading the file from AJAX, I pass the request on new separate window. Then, my servlet is called and file is getting downloaded on my local. Also, new window is closed when file starting downloaded
I guess you need to add following code and you should be good to go.
response.setContentType("application/octet-stream");
response.setContentLength((int) downloadFile.length());
// set headers for the response
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", AppUtility.getConvertedString(fileName)); //to ensure that there are no space in the file name
response.setHeader(headerKey, headerValue);
Following the advice of #dsp_user I used a to call the servlet and it works perfectly
var doc = document.getElementById("windowDocumentId").value;
var index = $("#lvDocuments").data("kendoListView").select().index();
var link = document.createElement("a");
link.download = $("#lvDocuments").data("kendoListView").dataSource.view()[index].itemText;
link.href = "./ServletDocuWindow?" + doc + "," +$("#lvDocuments").data("kendoListView").dataSource.view()[index].itemText;
link.click();
Related
I am using javascript and the Flask framework
I would like to retrieve in Flask in python the bytes of one or more files that the user will have chosen in my HTML page. To do this, when the user has chosen the files and has clicked on the send button, it triggers a function that uses the FileReader API to retrieve the base64 content of the file(s) that the user has selected.
After that, I would like to send the base64 data to flask with ajax.
But here is my problem, when I get the base64 string in python and compare it with the one in javascript, I notice the number of characters is exactly the same, but some characters are different as you can see on the screenshots below
size of character strings
And when I decode the base64 string, I get different bytes :
bytes variables
This makes me think that the problem is with the use of ajax
Code Python :
#app.route('/test', methods=['POST'])
def test():
if request.method == "POST":
files = eval(request.form.get("files"))
python_data = file.get("data")
javascript_data = "" # Copy from chrome console
len_python_data = len(python_data)
len_javascript_data = len(javascript_data)
base64_bytes_javascript = base64.b64decode(javascript_data)
base64_bytes_python = base64.b64decode(python_data)
Code Javascript :
let array_files = [];
let files_input = document.getElementById("files_input");
let files = files_input.files;
let reader = new FileReader();
function readFile(index) {
if( index >= files.length ) {
let data = "files=" + JSON.stringify(array_files);
$.ajax({
url: '/test',
type: 'POST',
data: data,
success: function (msg) {
console.log(msg)
}
});
return;
}
let file = files[index];
reader.name = file.name;
reader.onload = function() {
let file_info = {};
// get file content
let bin = this.result;
console.log(bin)
let data = bin.split(";")[1].replace("base64,", "");
file_info.name = reader.name;
file_info.data = data;
array_files.push(file_info);
readFile(index + 1)
}
reader.readAsDataURL(file);
}
readFile(0);
The problem is solved
In the base64 character string, the "+" were replaced by spaces after sending it to ajax.
Because of server issues I need to convert the contents of a file upload to Base64 before it gets submitted to the server.
I've managed the JS side of things using reader.readAsDataURL to get the contents into Base64 in a local JS variable. I've then tried creating a new FormData and setting the base64 variable there - it replaces the name of the but then it also replaces the type - it's no longer a but just binary data - so when I send this to the server - I'm getting Spring error typeMismatch.org.springframework.web.multipart.MultipartFile
Basically - any thoughts how to convert file contents to Base64 (done that ok) but send to existing JAVA method with Spring MultiPartFile?
i.e. without me rewriting and adding extra fields in the FormData for file name and size etc (the stuff I'd get using the MultipartFile on the server end).
JS: (error handling removed)
var input = $(".actualFileInput");
var files = null;
// File data
if (input[0].files.length > 0) {
files = input[0].files;
}
var file = files[0], reader = new FileReader();
reader.onloadend = function () {
var b64 = reader.result.replace(/^data:.+;base64,/, '');
var name = input.attr('name');
input.attr('name', '');
var newFormData = new FormData(form); // New form with all data from the existing one
newFormData.set('uploadFile',b64); // replace with the base64 value of the selected file
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
request.open(form.method, form.action, true);
request.onload = function() {
var url = window.location;
input.attr('name', name);
request.send(newFormData);
};
reader.readAsDataURL(file);
The Java at the server end:
#RequestMapping(method = RequestMethod.POST, params = "upload")
public String upload(#ModelAttribute("uploadDocument") UploadDocument document, BindingResult result,
ModelMap model, HttpServletRequest request, SessionStatus status) throws Exception {
UploadDocument is:
public class UploadDocument implements Serializable {
private static final long serialVersionUID = -3534003705995293025L;
// File upload work area
private MultipartFile uploadFile = null;
private String fileComment = null;
private Integer fileTypeId = null;
... (other fields in the <form>)
All the JAVA stuff works fine if I just submit the form. But in JS reading the file contents as Base64 then sending as a field doesnt get translated to MultiPartFile. Change to byte[] and add new fields for the file metadata myself? Or is there some trick I'm missing.
Thanks folks.
The way to make this JS variable send to a Spring MultiPartFile is:
newFormData.set('uploadFile',new Blob([b64]),files[0].name); // replace with the base64 value of the selected file
i.e. make it a blob, and 3rd arg is the file name the user selected in . This now sends the base64 value of the file contents.
Using TinyMCE 4, I am trying to do a basic local file picker such as the one used in their example.
After running their example, I noticed that the generated image source is a blob opposed to a base64.
So my question: is it possible to use base64 instead of a blob?
I thought the first argument of the file_picker_callback callback would be used as the source of the image, so I tweaked the code using this answer where I pass the data URI as the first argument.
file_picker_types: 'image',
// and here's our custom image picker
file_picker_callback: function (cb, value, meta) {
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
// Note: In modern browsers input[type="file"] is functional without
// even adding it to the DOM, but that might not be the case in some older
// or quirky browsers like IE, so you might want to add it to the DOM
// just in case, and visually hide it. And do not forget do remove it
// once you do not need it anymore.
input.onchange = function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
// Note: Now we need to register the blob in TinyMCEs image blob
// registry. In the next release this part hopefully won't be
// necessary, as we are looking to handle it internally.
//var id = 'blobid' + (new Date()).getTime();
//var blobCache = tinymce.activeEditor.editorUpload.blobCache;
//var base64 = reader.result.split(',')[1];
//var blobInfo = blobCache.create(id, file, base64);
//blobCache.add( blobInfo );
// call the callback and populate the Title field with the file name
cb(reader.result, { title: 'hola' });
};
reader.readAsDataURL( file );
};
input.click();
}
However it did not work and instead converted the source into a blob e.g.
<img src="blob:null/c8e90adb-4074-45b8-89f4-3f28c66591bb" alt="" />
If I pass a normal string e.g. test.jpg, it will generate
<img src="test.jpg" alt="" />
The blob: format you see is actually a Base64 encoded binary image. If you were to post the content of TinyMCE to the server you would indeed get the Base64 data.
You can force TinyMCE to immediately send that image to your server to get converted to a "regular" image by following these steps:
https://www.tinymce.com/docs/advanced/handle-async-image-uploads/
Add the below code inside the tinymce\plugins\quickbars\plugin.js at the position as shown in the image
$.ajax({
url: 'saveupload', // Upload Script
enctype : 'multipart/form-data',
type: 'post',
data: {"imageString":base64,"imageType":blob.type,"imageName": blob.name},
success: function(responseText) {
var myJSON = JSON.parse(responseText);
editor.insertContent(editor.dom.createHTML('img', { src: myJSON }));
},
error : function(xhr, ajaxOptions, thrownError) {
}
});
Note: If you ur using minified version convert the same into minified version using any minified tools (e.g: yuicompressor)
I am upload images to the apache
servlet code is below
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, java.io.IOException {
tbaService = new TBAServiceImpl();
File f = new File("path");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Map<String, String[]> parameterNames = request.getParameterMap();
Gson gson = new Gson();
HttpSession session = request.getSession(true);
long timeinMill = new Date().getTime();
String uniqueFileName = "local_"+timeinMill+"_"+parameterNames.get("imageName")[0].replace(" ", "_");
String fileType = parameterNames.get("imageType")[0].split("/")[1];
try {
BufferedImage image = null;
byte[] imageByte;
BASE64Decoder decoder = new BASE64Decoder();
imageByte = decoder.decodeBuffer(parameterNames.get("imageString")[0]);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bis);
bis.close();
// write the image to a file
File outputfile = new File(filePath+uniqueFileName); //filePath = C:/Apache/htdocs/tba/images/
ImageIO.write(image, fileType, outputfile);
out.print(gson.toJson(uploadUrl+uniqueFileName)); //uploadUrl=http://localhost/test/images/
out.flush();
out.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
I am using Spring Boot as backend server and I have a JavaScript frontend.
For sending data between front- and backend I'm using the Axios library, which usually works pretty fine.
The Problem:
The image looks like this in the (Chrome) browser console:
It's a very very long alphanumeric string and that's what I send to the server with the following code:
static uploadFiles(files) {
const data = new FormData();
Object.keys(files).forEach(key => {
data.append("files", new Blob([files[key]], { type: 'image/jpeg' }));
});
const url = API_URL + "uploadFiles";
return axios.post(url, data, RestServices.getAuth({
"Content-Type": "multipart/form-data;boundary=gc0p4Jq0M2Yt08jU534c0p"
}));
}
I have no idea what the boundary thing does but it worked to receive a file in the backend tho...
On backend (spring) side I successfully receive an array of MultipartFiles:
#RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
#ResponseBody
public boolean uploadFiles(HttpServletRequest request, #RequestParam("files") MultipartFile[] files) throws IOException {
String filePath = Thread.currentThread().getContextClassLoader().getResource("assets/images/").getFile();
InputStream inputStream;
OutputStream outputStream;
for(MultipartFile file : files) {
File newFile = new File(filePath + file.getOriginalFilename() + ".jpg");
inputStream = file.getInputStream();
if (!newFile.exists() && newFile.createNewFile()) {
outputStream = new FileOutputStream(newFile);
int read;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
}
System.out.println(newFile.getAbsolutePath());
}
return true;
}
I've also tried it file.transferTo(newFile); instead of in- and outputstreams - which didn't work either.
After that I get the following output, which means that the image was saved successfully:
/path/to/blob.jpg
If I check the path where the file was uploaded, there is a file named blob.jpg, but if I open it, the windows photo viewer has the following problem:
I've opened the image before and after upload with notepad++:
Before upload:
I think this is a byte array, but If I open the image after upload I get exactly the output of the browser. This means it didn't get converted to a byte array (correct me if I'm wrong) and I believe that's why it's a corrupt image...
My questions are:
What's the problem?
How can I fix it?
I really tried everything which crossed my mind but I ran out of ideas.
Thanks for your help! :-)
I've read following *related* questions (but they **don't** have an answer):
[Question1][5], [Question2][6], and **many** more...
I've finally found an answer on my own!
I think the problem was that I used the e.target.result (which is used to show the image on the frontend) but insted I had to use the JS File object. The standard HTML 5 file input fields return those File objects (as I've read here).
The only thing I had to do now is to make a FormData object, append the File Object, set the FormData as Body and set the Content-Type header and that's it!
const data = new FormData();
data.append("files", fileObject);
return axios.post(url, data, {
"Content-Type": "multipart/form-data"
});
Those JS File Objects are recognized from Java as Multipart files:
#RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
#ResponseBody
public boolean uploadFiles(HttpServletRequest request, #RequestParam("files") MultipartFile[] files) {
boolean transferSuccessful = true;
for (MultipartFile file : files) {
String extension = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));
String newFileName = genRandomName() + extension; //set unique name when saving on server
File newFile;
File imageFolder = new File(imageBasePath);
//check if parent folders exist else create it
if(imageFolder .exists() || imageFolder .mkdirs()) {
while ((newFile = new File(imageFolder .getAbsolutePath() + "\\" + newFileName)).exists()) {
newFileName = genRandomName(); //generate new name if file already exists
}
try {
file.transferTo(newFile);
} catch (IOException e) {
e.printStackTrace();
transferSuccessful = false;
}
} else {
LOG.error("Could not create folder at " + imageFolder.getAbsolutePath());
transferSuccessful = false;
}
}
return transferSuccessful;
}
I hope this is helpful :)
Trying to send a PDF from a servlet to the client. The client sends a POST request through AJAX, and the request is handled by the servlet to generate a PDF and sends the PDF as a response to the client.
I've tried the options posted here to no avail (getting empty/un-openable pdfs): Opening PDF String in new window with javascript
Any help would be apprecieated!
So far, I can only get a formatted PDF String printing in the browser console using this code:
Java Servlet:
response.setContentType("application/pdf");
response.setHeader("Content-disposition","attachment; filename=ProgressReport.pdf");
response.setContentLength((int) pdfFile.length());
OutputStream out = response.getOutputStream();
FileInputStream in = new FileInputStream(pdfFile);
byte[] buffer = new byte[1024];
int length =0;
while ((length = in.read(buffer)) != -1){
out.write(buffer, 0, length);
System.out.println(buffer);
}
in.close();
out.flush();
JS
$.ajax({
url : "GenerateReport",
data : {
...
},
type : "POST",
success : function(result) {
console.log(result);
//window.open("data:application/pdf, " + encodeURI(result));
//download(result);
},
error : function(result) {
...
}
})
PDF String in Browser Console
%PDF-1.4 %����3 0 obj<</Filter/FlateDecode/Length 238>>streamx��QMO�#��x��(��D��!A�x�R��T�-�n��{�5LDB�e2�y�>2�l�Y$1�:a�i.�"�~f ...
I'm not 100% but your window.open is not the best as pop up blockers might prevent it as it's not a user action calling it's an AJAX response.
The better way would be the method outlined in this answer
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:attachment/text,' + encodeURI(result);
hiddenElement.target = '_blank';
hiddenElement.download = 'myFile.txt';
hiddenElement.click();
The other option is to use Base64 encoding and use "data:image/png;base64,"+result and in your C# you would need to create the buffer the size of the file and then base64 encode the entire buffer