I am using the Tomahawk inputFileUpload component to allow users to upload files to a server. I have implemented a "soft" file size limit by checking the size of the file after it has been uploaded and displaying an error if it is too large. However I would also like a larger "hard" limit, where uploading immediately stops once it has passed the limit. For example if the hard limit is 500MB and the user attempts to upload a 2GB file, uploading will immediately stop once 500MB has been uploaded and an error is displayed.
I had hoped that using the MyFaces ExtensionsFilter and setting uploadMaxFileSize would fix the problem, but the file is completely uploaded before the SizeLimitExceededException is thrown.
Is it possible to do this? Ideally I'd still be able to use Tomahawk but any other solution would be good.
The web server can't abort a HTTP request halfway and then return a HTTP response. The entire HTTP request has to be consumed fully until the last bit before a HTTP response can ever be returned. That's the nature of HTTP and TCP/IP. There's nothing you can do against it with a server side programming language.
Note that the Tomahawk file upload size limit already takes care that the server's memory/disk space won't be polluted with the entire uploaded file whenever the size limit has been hit.
Your best bet is to validate the file length in JavaScript before the upload takes place. This is supported in browsers supporting HTML5 File API. The current versions of Firefox, Chrome, Safari, Opera and Android support it. IE9 doesn't support it yet, it'll be in the future IE10.
<t:inputFileUpload ... onchange="checkFileSize(this)" />
with something like this
function checkFileSize(inputFile) {
var max = 500 * 1024 * 1024; // 500MB
if (inputFile.files && inputFile.files[0].size > max) {
alert("File too large."); // Do your thing to handle the error.
inputFile.value = null; // Clears the field.
}
}
Try this:
<div>
<p:fileUpload id="fileUpload" name="fileUpload" value="#{controller.file}" mode="simple" rendered="true"/>
<input type="button" value="Try it" onclick="checkFileSize('fileUpload')" />
</div>
When user click in the button "Try it", checkFileSize() function is called and the fileUpload primefaces component is validated. If file size is greater than 500MB the file not is uploaded.
<script>
// <![CDATA[
function checkFileSize(name) {
var max = 500 * 1024 * 1024; // 500MB
var inputFile = document.getElementsByName(name)[0];
var inputFiles = inputFile.files;
if (inputFiles.lenght > 0 && inputFiles[0].size > max) {
alert("File too large."); // Do your thing to handle the error.
inputFile.value = null; // Clears the field.
}
}
// ]]>
</script>
The checkFileSize() logic is based on the BalusC answered above.
Versions tested:
primefaces 3.5
jsf 2.1
Related
I want to be able to check for the size of uploaded files.
And if it exceeds a limit, i want to be able to issue my REST API errors.
I have this code:
app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024
If you try to upload a file that is larger than 1MB, the application will now refuse it.
But this does not give me a lot of control, in terms of the REST API message that my app will generate.
So how can i check for max size of uploaded file, and issue my personal message, along with an HTTP status code, whenever that happens?
EDIT:
Now i receive this on my curl terminal (client side)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>413 Request Entity Too Large</title>
<h1>Request Entity Too Large</h1>
<p>The data value transmitted exceeds the capacity limit.</p>
While i would like to send something like this (server side code):
return make_response(json.dumps({'error_message': 'file size too large'}), 413)
Solved it with this:
#app.errorhandler(413)
def request_entity_too_large(error):
return make_response(json.dumps({'error_message': 'file size too large'}), 413)
Added error handler
I am using Primefaces to provide file download action in JSF. The code I am using is:
<p:commandButton id="xlsExport" value="Export XLS"
ajax="false"
onclick="PrimeFaces.monitorDownload(startPleaseWaitMonitor, stopPleaseWaitMonitor);">
<p:fileDownload value="#{SampleBean.XLSExport}" />
</p:commandButton>
SampleBean has the following method:
public StreamedContent getXLSExport() {
...
byte[] content = generator.generateXLS();
return new DefaultStreamedContent(new ByteArrayInputStream(content), "application/vnd.ms-excel", fileName, "UTF-8");
}
I am using it on two application servers - JBoss and Websphere. In case of Websphere I see warning in server logs when I do export:
000000f5 SRTServletRes W
com.ibm.ws.webcontainer.srt.SRTServletResponse setStatus WARNING:
Cannot set status. Response already committed.
When I run similar method but for CSV export there is no warning. For JBoss there is no warning too.
What could be the reason for such log warning?
I've recreated this locally - it appears that PrimeFaces' FileDownloadActionListener is attempting to set the response status code after that response has already been committed by the server. The FileDownload code grabs the response output stream, writes the entire contents of the downloaded file to it, and then attempts to update the response status code.
WebSphere commits and flushes a response when the amount of data passed into the response buffer exceeds a certain threshold (by default 32K.) Once the response has been committed, its headers (eg. status code) can't be updated. The other application servers probably behave the same way here - they just might not log a warning message. In this particular case the warning isn't anything to worry about, since the FileDownload code was just attempting to update the status code from 200 -> 200.
Using different content types (like CSV) shouldn't make a difference here. File size does make a difference - if a file is downloaded that's less than the response buffer size, then the response won't be committed before the PrimeFaces code tries to set its status.
A simple fix for this warning message would be to check to see if the response is committed before attempting to change its status. I've opened a PrimeFaces issue for this: https://github.com/primefaces/primefaces/issues/3955
Update: I provided a fix to PrimeFaces, so you shouldn't see this anymore in the nightly builds/next version.
I'm using multer v1.3.0 with express v4.15.4.
I have used fileSize limits like below
multer({
storage: storage,
limits: {fileSize: 1*1000*50, files:1},
fileFilter: function (req, file, cb) {
if (_.indexOf(['image/jpeg', 'image/png'], file.mimetype) == -1)
cb(null, false);
else
cb(null, true)
}
});
In this case I dont think limits are working. Cause not getting any
LIMIT_FILE_SIZE error.
If I remove fileFilter then I'm getting LIMIT_FILE_SIZE error.
But in both cases first the whole file getting uploaded & then fileSize is checked.
Its not good at all to upload a 1GB of file and then check its of 1MB or not.
So I want to know if there has any way to stop the uploading in the middle when file size limit exceeds. I don't want to rely on Content-Length.
From looking through the multer and busboy source it looks like it calculates the data size as a running total and stops reading the stream as soon as the fileSize is reached. See the code here:
https://github.com/mscdex/busboy/blob/8f6752507491c0c9b01198fca626a9fe6f578092/lib/types/multipart.js#L216
So while 1GB might be uploaded it shouldn't be saved anywhere.
If you want to stop the upload from continuing I think you'd have to close the socket though I haven't experimented with this myself. I'm not aware of any way to respond cleanly while the request is still trying to upload data.
Related:
YES or NO: Can a server send an HTTP response, while still uploading the file from the correlative HTTP request?
You can use a javascript script to prevent users uploading all of the 1GB and getting a file size exceeded error. However, all client-side checks can be bypassed, so you should still enforce the file limit on the backend.
Your code is correct, it should work as intended. I am guessing you are worried about the file getting uploaded. There are no workarounds for that since Multer checks the size after upload.
Here is the javascript you can put in to prevent someone from uploading for your client-side code.
function ValidateSize(file) {
const FileSize = file.files[0].size / 1024 / 1024;
if (FileSize > 20) {
alert('File size exceeds 20 MB');
document.getElementById('formFile').value = null;
}
}
<label for="formFile" class="form-label">Select file to upload (Max File Size: 20MB):</label>
<input class="form-control" type="file" onchange="ValidateSize(this)" id="formFile" name="file">
I am working on the project which simply read the txt/csv files from the directory and store in the database.
The upload function works like a charm but the problem is that I want to display the progress - any notification that tells the users that the system is working on the files.
Here is my HTML markup
<div class="form">
<input type="file" id="uploadFile" />
<button id="submit" type="button">Upload</button>
</div>
<div class="terminal">
<! -------------------RESULT---------------------->
</div>
I have the div called terminal to display the progress of file uploading. The way I place text inside the div is to use the innerHTML function of javascript. Thus, my upload code is as follows
var files = fs.readdirSync(folder_path);
// Loop through files gathered from folder reading
for(i=0; i<files.length; i++) {
document.getElementbyId("terminal").innerHTML+=files[i]+"<br/>";
var fileData = fs.readfileSync(folder_path+'\'+files[i], 'utf8');
// Upload function below
}
The above markup is a part of my js file which serves as an uploader. I first read the folder with fs.readdirSync, then iterate through the array of files gathered from readdirSync function. I want to display the file name in each loop in real time.
But it just displays everything once the upload has finished. So I don't know how to make it display the progress in real time or in other word, just like the console which displays any information in real time.
You don't have to loop, you can easily port this to your frontend app:
Monitoring XHR Progress
https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress
I am using Primefaces Multiple file upload component in an application. Here i choose 'n' number of files and clicked on upload button. Then i need to get each files in fileUploadListener according to alphabetical order. How it possible?
As the multiple file upload component is a jQuery-File-Upload plugin, the default state is not sequential, that means all the files get upload asynchronously.
To get the component to do a sequential upload, you have to set sequentialUploads to true, and on change we do a little alphabetical sorting of the current files. all this is done by javascript.
Assuming your widgetVar is fileUploadWV
<p:fileUpload widgetVar="fileUploadWV"
fileUploadListener="#{attachmentBean.onUpload}" />
<script>
$(function() {
// setTimeout waits till the widgetVar is ready!
setTimeout(sortFileUpload, 2000);
});
function sortFileUpload() {
//Set this option to true to issue all file upload requests in a sequential order instead of simultaneous requests.
PF('fileUploadWV').jq.data().blueimpFileupload.options.sequentialUploads = true;
//every time a new file is added, sort the files based on name
PF('fileUploadWV').jq.change(function() {
PF('fileUploadWV').files.sort(function fileSort(a, b) {
return a.name.localeCompare(b.name)
})
});
}
</script>
So in this scenario your files would get uploaded in an alphabetical order.
Note: if you don't set sequentialUploads into true, you have no control which file is going to be sent first.
Github, Online Demo
Hope this helps.
yes, but this solution is not very elegant:
<p:remoteCommand action="#{attachmentBean.processAttachments}"
name="processAttachments" update="attachmentTable"/>
<p:fileUpload fileUploadListener="#{attachmentBean.onUpload}"
oncomplete="processAttachments()" />
attachmentBean.onUpload stores each file inside a List/Map/SortedMap
attachmentBean.processAttachments eventually sorts that List/Map and process attachments in order
attachmentBean must be at least #ViewScoped