问题
I have a multiple files upload form:
<input type="file" name="files" multiple />
I am posting these files with ajax. I would like to upload the selected files one by one (to create individual progress bars, and out of curiousity).
I can get the list of files or individual files by
FL = form.find('[type="file"]')[0].files
F = form.find('[type="file"]')[0].files[0]
yieling
FileList { 0=File, 1=File, length=2 }
File { size=177676, type="image/jpeg", name="img.jpg", more...}
But FileList is immutable and I can't figure out how to submit the single file.
I think this is possible as I saw http://blueimp.github.com/jQuery-File-Upload/. I don't want to use this plugin however as it's as much about learning as the result (and it would need too much custimizing anyway). I also don't want to use Flash.
回答1:
For this to be a synchronous operation, you need to start the new transfer when the last one is done. Gmail, for example, send everything at once, concurrently.
The event for the progress on AJAX file upload is progress
or onprogress
on the raw XmlHttpRequest
instance.
So, after each $.ajax()
, on the server side (which I don't know what you'll be using), send a JSON response to execute the AJAX on the next input. One option would to bind the AJAX element to each element, to make things easier, so you could just do, in the success
the $(this).sibling('input').execute_ajax()
.
Something like this:
$('input[type="file"]').on('ajax', function(){
var $this = $(this);
$.ajax({
'type':'POST',
'data': (new FormData()).append('file', this.files[0]),
'contentType': false,
'processData': false,
'xhr': function() {
var xhr = $.ajaxSettings.xhr();
if(xhr.upload){
xhr.upload.addEventListener('progress', progressbar, false);
}
return xhr;
},
'success': function(){
$this.siblings('input[type="file"]:eq(0)').trigger('ajax');
$this.remove(); // remove the field so the next call won't resend the same field
}
});
}).trigger('ajax'); // Execute only the first input[multiple] AJAX, we aren't using $.each
The above code would be for multiple <input type="file">
but not for <input type="file" multiple>
, in that case, it should be:
var count = 0;
$('input[type="file"]').on('ajax', function(){
var $this = $(this);
if (typeof this.files[count] === 'undefined') { return false; }
$.ajax({
'type':'POST',
'data': (new FormData()).append('file', this.files[count]),
'contentType': false,
'processData': false,
'xhr': function() {
var xhr = $.ajaxSettings.xhr();
if(xhr.upload){
xhr.upload.addEventListener('progress', progressbar, false);
}
return xhr;
},
'success': function(){
count++;
$this.trigger('ajax');
}
});
}).trigger('ajax'); // Execute only the first input[multiple] AJAX, we aren't using $.each
回答2:
This looks like a very good tutorial, just what you're looking for.
That noted, uploading via vanilla ajax isn't supported in all browsers, and I would still recommend using an iframe
. (I.E dynamically create an iframe and POST it using javascript)
I have always been using this script and I find it very concise. If you want to learn how to upload via creating an iframe, you should dig through it's source.
Hope it helps :)
Extra edit
To create progress bars with the iframe method, you will need to do some work server-side. If you're using php, you can use:
- Session upload progress for php5.4+
- Upload progress package for php<5.4
If use nginx you can also choose to compile with their Upload progress module
All of these work the same way - every upload has a UID, you will request for the 'progress' associated with that ID at consistent intervals via ajax. This will return the upload progress as determined by the server.
回答3:
I was facing same problem and came with this solution. I simply retrieve form with multipart, and in recursive call ( file after file ) start ajax request, which only calls next when is done.
var form = document.getElementById( "uploadForm" );
var fileSelect = document.getElementById( "photos" );
var uploadDiv = document.getElementById( "uploads" );
form.onsubmit = function( event ) {
event.preventDefault( );
var files = fileSelect.files;
handleFile( files, 0 );
};
function handleFile( files, index ) {
if( files.length > index ) {
var formData = new FormData( );
var request = new XMLHttpRequest( );
formData.append( 'photo', files[ index ] );
formData.append( 'serial', index );
formData.append( 'upload_submit', true );
request.open( 'POST', 'scripts/upload_script.php', true );
request.onload = function( ) {
if ( request.status === 200 ) {
console.log( "Uploaded" );
uploadDiv.innerHTML += files[ index ].name + "<br>";
handleFile( files, ++index );
} else {
console.log( "Error" );
}
};
request.send( formData );
}
}
回答4:
Using this source code you can upload multiple file like google one by one through ajax. Also you can see the uploading progress
HTML
<input type="file" id="multiupload" name="uploadFiledd[]" multiple >
<button type="button" id="upcvr" class="btn btn-primary">Start Upload</button>
<div id="uploadsts"></div>
Javascript
<script>
function uploadajax(ttl,cl){
var fileList = $('#multiupload').prop("files");
$('#prog'+cl).removeClass('loading-prep').addClass('upload-image');
var form_data = "";
form_data = new FormData();
form_data.append("upload_image", fileList[cl]);
var request = $.ajax({
url: "upload.php",
cache: false,
contentType: false,
processData: false,
async: true,
data: form_data,
type: 'POST',
xhr: function() {
var xhr = $.ajaxSettings.xhr();
if(xhr.upload){
xhr.upload.addEventListener('progress', function(event){
var percent = 0;
if (event.lengthComputable) {
percent = Math.ceil(event.loaded / event.total * 100);
}
$('#prog'+cl).text(percent+'%')
}, false);
}
return xhr;
}
})
.success(function(res,status) {
if(status == 'success'){
percent = 0;
$('#prog'+cl).text('');
$('#prog'+cl).text('--Success: ');
if(cl < ttl){
uploadajax(ttl,cl+1);
}else{
alert('Done ');
}
}
})
.fail(function(res) {
alert('Failed');
});
}
$('#upcvr').click(function(){
var fileList = $('#multiupload').prop("files");
$('#uploadsts').html('');
var i
for ( i = 0; i < fileList.length; i++) {
$('#uploadsts').append('<p class="upload-page">'+fileList[i].name+'<span class="loading-prep" id="prog'+i+'"></span></p>');
if(i == fileList.length-1){
uploadajax(fileList.length-1,0);
}
}
});
</script>
PHP
upload.php
move_uploaded_file($_FILES["upload_image"]["tmp_name"],$_FILES["upload_image"]["name"]);
回答5:
Html Code
<div class="row">
<form id="singleUploadForm" name="singleUploadForm" style="float: right; padding-right: 25px">
<input style="float: left;" id="singleFileUploadInput" type="file" name="files" class="file-input btn btn-default" required multiple/>
<button style="float: left" type="submit" class="btn .btn-primary">Upload</button>
</form>
</div>
JavaScript Code
$('#mutipleUploadForm').submit(function (event) {
var formElement = this;
var formData = new FormData(formElement);
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/uploadFile",
data: formData,
processData: false,
contentType: false,
success: function (response) {
alert("Image Uploaded Successfully");
location.reload();
},
error: function (error) {
console.log(error);
// process error
}
});
event.preventDefault();
});
Spring Controller Class
@PostMapping("/uploadFile")
@ResponseBody
public boolean uploadMutipleFiles(@RequestParam("file") MultipartFile[] file) {
List<Boolean> uploadStatusList = new ArrayList<>();
for (MultipartFile f : file) {
uploadStatusList.add(uploadFile(f));
}
return !uploadStatusList.contains(false);
}
public boolean uploadFile(@RequestParam("file") MultipartFile file) {
boolean uploadStatus = galleryService.storeFile(file);
return uploadStatus;
}
Spring Service Class
public boolean storeFile(MultipartFile file) {
boolean uploadStatus = false;
try {
String fileName = file.getOriginalFilename();
InputStream is = file.getInputStream();
Files.copy(is, Paths.get(imagesDirectory + fileName), StandardCopyOption.REPLACE_EXISTING);
System.out.println("File store success");
uploadStatus = true;
} catch (Exception e) {
e.printStackTrace();
}
return uploadStatus;
}
来源:https://stackoverflow.com/questions/13656066/html5-multiple-file-upload-upload-one-by-one-through-ajax