使用jQuery+PHP多图上传时服务器无法接收文件的问题
问题:Ajax+PHP多图上传,表单文本正常提交但服务器接收文件数组为空
我用Ajax和PHP实现多图上传功能,前端能动态生成上传框,最多支持5张图片,前端交互功能正常,表单里的文本内容也能正常提交到服务器,但PHP端接收的文件数组始终为空,排查代码没找到问题,求解决。
前端HTML代码
<div class="upload-container" id="uploadContainer"> <div class="upload-box show"> <span class="plus-sign">+</span> <input type="file" name="images[]" accept="image/*" multiple> <button class="delete-icon" type="button">×</button> </div> </div>
修正后的前端JavaScript代码
document.addEventListener('DOMContentLoaded', function() { const uploadContainer = document.getElementById('uploadContainer'); const imageUploadForm = document.getElementById('form-review'); let imageCount = 0; const maxImages = 5; let selectedFiles = []; // 新增全局数组存储选中的文件 function createUploadBox() { if (imageCount >= maxImages) return; const uploadBox = document.createElement('div'); uploadBox.classList.add('upload-box'); uploadBox.innerHTML = ` <span class="plus-sign">+</span> <input type="file" accept="image/*" name="images[]" multiple> <button class="delete-icon" type="button">×</button> `; uploadContainer.appendChild(uploadBox); setTimeout(() => uploadBox.classList.add('show'), 10); attachUploadEvents(uploadBox); } function attachUploadEvents(uploadBox) { const input = uploadBox.querySelector('input[type="file"]'); const deleteIcon = uploadBox.querySelector('.delete-icon'); uploadBox.addEventListener('click', () => { if (uploadBox.classList.contains('filled')) return; input.click(); }); input.addEventListener('change', (event) => { const files = Array.from(event.target.files); const validFiles = files.slice(0, maxImages - imageCount); // 将选中的文件存入全局数组 selectedFiles.push(...validFiles); validFiles.forEach(file => { const reader = new FileReader(); reader.onload = (e) => { const imgBox = document.createElement('div'); imgBox.classList.add('upload-box', 'show', 'filled'); imgBox.innerHTML = ` <img src="${e.target.result}" alt="Image"> <button class="delete-icon" type="button">×</button> `; uploadContainer.insertBefore(imgBox, uploadContainer.lastElementChild); // 绑定删除事件时传入对应文件,方便后续移除 attachDeleteEvent(imgBox, file); imageCount++; if (imageCount >= maxImages) { const emptyUploadBox = document.querySelector('.upload-box:not(.filled)'); if (emptyUploadBox) emptyUploadBox.remove(); } }; reader.readAsDataURL(file); }); input.value = ''; }); deleteIcon.addEventListener('click', (event) => { event.stopPropagation(); event.preventDefault(); uploadBox.classList.remove('show'); setTimeout(() => { uploadBox.remove(); imageCount--; ensureOneEmptyUploadBox(); }, 300); }); } window.resetUploadBoxes = function() { const filledUploadBoxes = document.querySelectorAll('.upload-box.filled'); filledUploadBoxes.forEach(box => box.remove()); imageCount = 0; selectedFiles = []; // 重置文件数组 ensureOneEmptyUploadBox(); } function attachDeleteEvent(uploadBox, file) { const deleteIcon = uploadBox.querySelector('.delete-icon'); deleteIcon.addEventListener('click', (event) => { event.stopPropagation(); event.preventDefault(); uploadBox.classList.remove('show'); setTimeout(() => { uploadBox.remove(); imageCount--; // 从全局数组中移除当前删除的文件 selectedFiles = selectedFiles.filter(f => f !== file); ensureOneEmptyUploadBox(); }, 300); }); } function ensureOneEmptyUploadBox() { const emptyUploadBox = document.querySelector('.upload-box:not(.filled)'); if (!emptyUploadBox && imageCount < maxImages) { createUploadBox(); } } attachUploadEvents(document.querySelector('.upload-box')); $('#button-review').on('click', function() { var formData = new FormData($('#form-review')[0]); // 清除FormData中可能存在的旧文件数据,避免重复 formData.delete('images[]'); // 将全局数组中的文件添加到FormData selectedFiles.forEach(file => { formData.append('images[]', file); }); $.ajax({ url: 'index.php?route=product/product/write&product_id={{ product_id }}', type: 'post', dataType: 'json', data: formData, cache: false, contentType: false, processData: false, beforeSend: function() { $('#button-review').button('loading'); }, complete: function() { $('#button-review').button('reset'); }, success: function(json) { $('.alert-dismissible').remove(); if (json['error']) { $('#review').after('<div class="alert alert-danger alert-dismissible"><i class="fa fa-exclamation-circle"></i> ' + json['error'] + '</div>'); $('html, body').animate({scrollTop: $("#review").offset().top}, 800); } if (json['success']) { resetUploadBoxes(); $('#review').after('<div class="alert alert-success alert-dismissible"><i class="fa fa-check-circle"></i> ' + json['success'] + '</div>'); $('html, body').animate({scrollTop: $("#review").offset().top}, 800); $('input[name=\'name\']').val(''); $('textarea[name=\'text\']').val(''); $('input[name=\'rating\']:checked').prop('checked', false); } }, error: function(xhr, ajaxOptions, thrownError) { alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText); } }); }); });
修正后的PHP代码
$files = []; // 针对name为images[]的文件数组进行处理 if (!empty($this->request->files['images']) && is_array($this->request->files['images'])) { foreach ($this->request->files['images'] as $file) { if (!empty($file['name'])) { $files[] = array( 'name' => $file['name'], 'type' => $file['type'], 'tmp_name' => $file['tmp_name'], 'error' => $file['error'], 'size' => $file['size'] ); } } }
问题原因及解决说明
- 核心问题:原代码中选中文件后执行了
input.value = ''清空了文件输入框,导致提交表单时遍历input获取不到文件;同时预览的图片盒子里没有文件输入控件,选中的文件根本没被加入到FormData中。 - 解决思路:用全局数组存储选中的所有文件,删除预览图时同步从数组移除对应文件,提交表单时统一将数组中的文件添加到FormData,确保文件能被正确传递到服务器。
- PHP端修正:原代码遍历整个
$this->request->files,改为直接获取images[]对应的文件数组,匹配前端传递的参数名。
内容的提问来源于stack exchange,提问作者afazolo
相关产品推荐
相关产品推荐

