最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot上傳文件與物理刪除功能

 更新時(shí)間:2026年01月27日 11:28:49   作者:zbguolei  
文章介紹了在Springboot中實(shí)現(xiàn)文件上傳和刪除的功能,實(shí)現(xiàn)了圖片的上傳與刪除,刪除數(shù)據(jù)庫(kù)記錄時(shí),同步刪除了文件,結(jié)合實(shí)例代碼給大家講解的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧

前端頁(yè)面--新增:

<!-- 文件上傳區(qū)域 -->
<div class="form-group">
    <label class="col-sm-3 control-label is-required">照片文件:</label>
    <div class="col-sm-8">
        <input type="hidden" name="photoPath" id="photoPathHidden">
        <!-- 使用 label 原生觸發(fā),覆蓋整個(gè)區(qū)域 -->
        <div id="drop-area" style="border: 2px dashed #ccc; padding: 20px; text-align: center; margin-bottom: 10px; cursor: pointer;">
            <label for="fileElem" style="width:100%; height:100%; display:block;">
                <p>拖拽圖片到這里,或點(diǎn)擊選擇文件</p>
            </label>
            <input type="file" id="fileElem" accept="image/*" style="display:none;" />
        </div>
        <div id="preview" style="margin-top: 10px;"></div>
        <button type="button" class="btn btn-primary btn-sm" id="uploadBtn" disabled>上傳圖片</button>
        <span id="uploadStatus" style="margin-left: 10px; color: green;"></span>
        <button type="button" class="btn btn-danger btn-sm" id="deleteBtn" style="display:none; margin-left:10px;">刪除圖片</button>
    </div>
</div>
<!-- 提交按鈕 -->
<div class="form-group">
    <div class="col-sm-offset-3 col-sm-8">
        <button type="button" class="btn btn-success" onclick="submitHandler()">確定</button>
    </div>
</div>
<script th:inline="javascript">
    var prefix = ctx + "system/record";
    // 初始化日期控件
    $("input[name='photoDate']").datetimepicker({
        format: "yyyy-mm-dd",
        minView: "month",
        autoclose: true
    });
    // 表單驗(yàn)證 & 提交
    $("#form-record-add").validate({
        focusCleanup: true
    });
    function submitHandler() {
        if (!$('#photoPathHidden').val()) {
            alert('請(qǐng)先上傳照片!');
            return;
        }
        if ($.validate.form()) {
            console.log('準(zhǔn)備提交到:', prefix + "/add");
            $.operate.save(prefix + "/add", $('#form-record-add').serialize());
        }
    }
    // ========== 文件上傳邏輯 ==========
    const dropArea = document.getElementById('drop-area');
    const fileElem = document.getElementById('fileElem');
    const preview = document.getElementById('preview');
    const uploadBtn = document.getElementById('uploadBtn');
    const uploadStatus = document.getElementById('uploadStatus');
    const deleteBtn = document.getElementById('deleteBtn');
    let selectedFile = null;
    // 文件選擇變化
    fileElem.addEventListener('change', handleFiles);
    // 拖拽事件
    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
        dropArea.addEventListener(eventName, preventDefaults, false);
    });
    function preventDefaults(e) {
        e.preventDefault();
        e.stopPropagation();
    }
    ['dragenter', 'dragover'].forEach(eventName => {
        dropArea.addEventListener(eventName, highlight, false);
    });
    ['dragleave', 'drop'].forEach(eventName => {
        dropArea.addEventListener(eventName, unhighlight, false);
    });
    function highlight() {
        dropArea.style.borderColor = '#007bff';
    }
    function unhighlight() {
        dropArea.style.borderColor = '#ccc';
    }
    dropArea.addEventListener('drop', handleDrop, false);
    function handleDrop(e) {
        const dt = e.dataTransfer;
        const files = dt.files;
        // 手動(dòng)構(gòu)造 event 對(duì)象傳給 handleFiles
        handleFiles({ target: { files: files } });
    }
    function handleFiles(e) {
        const files = e.target.files;
        if (files.length === 0) return;
        selectedFile = files[0];
        // 驗(yàn)證類(lèi)型
        if (!selectedFile.type.match('image.*')) {
            alert('請(qǐng)選擇圖片文件(jpg/png/jpeg)');
            selectedFile = null;
            uploadBtn.disabled = true;
            // 清空 input,以便下次可重新選擇(包括同名文件)
            fileElem.value = '';
            return;
        }
        // 預(yù)覽縮略圖
        preview.innerHTML = '';
        const img = document.createElement('img');
        img.src = URL.createObjectURL(selectedFile);
        img.style.maxWidth = '200px';
        img.style.maxHeight = '200px';
        preview.appendChild(img);
        uploadBtn.disabled = false;
        uploadStatus.textContent = '';       
        // 因?yàn)榭赡苡脩粝胫匦律蟼魍晃募ǖǔ2粫?huì)),所以暫不在此清空
    }
    // 上傳按鈕點(diǎn)擊
    uploadBtn.addEventListener('click', () => {
        if (!selectedFile) return;
    const formData = new FormData();
    formData.append("file", selectedFile);
    // ? 關(guān)鍵修復(fù):使用 ctx 上下文路徑,而非硬編碼 /
    fetch(ctx + 'common/upload', {
        method: 'POST',
        body: formData
    })
        .then(response => response.json())
    .then(data => {
        if (data.code === 0) {
        $('#photoPathHidden').val(data.fileName);
        uploadStatus.textContent = '? 上傳成功';
        uploadBtn.disabled = true;
        deleteBtn.style.display = 'inline-block';
        // 上傳成功后清空 file input,避免后續(xù)干擾
        fileElem.value = '';
    } else {
        alert('上傳失?。? + (data.msg || '未知錯(cuò)誤'));
        uploadStatus.textContent = '? 上傳失敗';
        // 失敗時(shí)也清空,允許重試
        fileElem.value = '';
    }
    })
    .catch(err => {
        console.error('上傳出錯(cuò):', err);
    alert('網(wǎng)絡(luò)錯(cuò)誤,請(qǐng)重試');
    uploadStatus.textContent = '? 網(wǎng)絡(luò)錯(cuò)誤';
    fileElem.value = ''; // 允許重試
    });
    });
    // 刪除按鈕點(diǎn)擊事件
    deleteBtn.addEventListener('click', function() {
        if (!confirm('確定要?jiǎng)h除這張照片嗎?')) return;
        const photoPath = $('#photoPathHidden').val();
        if (photoPath) {
            fetch(ctx + 'common/deleteFile?fileName=' + encodeURIComponent(photoPath), {
                method: 'POST'
            })
                .then(response => response.json())
        .then(data => {
                if (data.code !== 0) {
                alert('服務(wù)器刪除失敗:' + (data.msg || ''));
            }
            clearPhotoPreview();
        })
        .catch(err => {
                console.error('刪除請(qǐng)求失敗:', err);
            alert('網(wǎng)絡(luò)錯(cuò)誤,但本地預(yù)覽已清除');
            clearPhotoPreview();
        });
        } else {
            clearPhotoPreview();
        }
    });
    // 清空預(yù)覽和狀態(tài)的函數(shù)
    function clearPhotoPreview() {
        selectedFile = null;
        $('#photoPathHidden').val('');
        preview.innerHTML = '';
        uploadStatus.textContent = '';
        uploadBtn.disabled = true;
        deleteBtn.style.display = 'none';
        // 同時(shí)清空文件輸入框
        fileElem.value = '';
    }
</script>

 修改com.ruoyi.project.common.CommonController的/deleteFile方法:

/**
 * 刪除文件
 * @param fileName
 * @return
 */
@PostMapping("/deleteFile")
@ResponseBody
public AjaxResult deleteFile(String fileName) {
    try {
        if (StringUtils.isBlank(fileName)) {
            return error("文件名為空");
        }
        if (fileName.contains("..") || !fileName.startsWith("/profile/")) {
            return error("非法文件路徑");
        }
        String profilePath = RuoYiConfig.getProfile();
        String relativePath = fileName.substring("/profile/".length());
        // ? 使用 Paths.get 自動(dòng)處理路徑分隔符
        String realPath = Paths.get(profilePath, relativePath).toString();
        File file = new File(realPath);
        if (file.exists()) {
            if (file.delete()) {
                return success();
            } else {
                return error("文件刪除失敗,可能被占用或無(wú)權(quán)限");
            }
        }
        return success(); // 文件不存在也視為成功(冪等)
    } catch (Exception e) {
        log.error("刪除文件異常", e);
        return error("系統(tǒng)異常:" + e.getMessage());
    }
}

前端頁(yè)面---編輯:

<!-- 文件上傳區(qū)域(復(fù)用新增頁(yè)邏輯) -->
<div class="form-group">
    <label class="col-sm-3 control-label is-required">照片文件:</label>
    <div class="col-sm-8">
        <!-- 隱藏域:存儲(chǔ)服務(wù)器路徑 -->
        <input type="hidden" name="photoPath" id="photoPathHidden" th:value="*{photoPath}">
        <!-- 拖拽區(qū)域 -->
        <div id="drop-area" style="border: 2px dashed #ccc; padding: 20px; text-align: center; margin-bottom: 10px; cursor: pointer;">
            <label for="fileElem" style="width: 100%; height: 100%; display: block;">
                <p>拖拽圖片到這里,或點(diǎn)擊選擇文件</p>
            </label>
            <input type="file" id="fileElem" accept="image/*" style="display:none;" />
        </div>
        <!-- 縮略圖預(yù)覽 -->
        <div id="preview" style="margin-top: 10px;"></div>
        <!-- 操作按鈕 -->
        <button type="button" class="btn btn-primary btn-sm" id="uploadBtn" disabled>上傳新圖片</button>
        <span id="uploadStatus" style="margin-left: 10px; color: green;"></span>
        <button type="button" class="btn btn-danger btn-sm" id="deleteBtn" style="display:none; margin-left:10px;">刪除當(dāng)前圖片</button>
    </div>
</div>
<script th:inline="javascript">
    var prefix = ctx + "system/record";
    // 初始化日期控件
    $("input[name='photoDate']").datetimepicker({
        format: "yyyy-mm-dd",
        minView: "month",
        autoclose: true
    });
    // 表單驗(yàn)證
    $("#form-record-edit").validate({ focusCleanup: true });
    function submitHandler() {
        if (!$('#photoPathHidden').val()) {
            alert('請(qǐng)保留或上傳一張照片后再提交!');
            return;
        }
        if ($.validate.form()) {
            $.operate.save(prefix + "/edit", $('#form-record-edit').serialize());
        }
    }
    // ========== 文件上傳邏輯 ==========
    const dropArea = document.getElementById('drop-area');
    const fileElem = document.getElementById('fileElem');
    const preview = document.getElementById('preview');
    const uploadBtn = document.getElementById('uploadBtn');
    const uploadStatus = document.getElementById('uploadStatus');
    const deleteBtn = document.getElementById('deleteBtn');
    let selectedFile = null;
    // 獲取初始 photoPath(Thymeleaf 渲染)
    const initialPhotoPath = /*[[${photoRecord.photoPath}]]*/'';
    // 初始化預(yù)覽(如果有已有圖片)
    function initPreview() {
        if (initialPhotoPath) {
            preview.innerHTML = '';
            const img = document.createElement('img');
            // ? 關(guān)鍵:如果 photoPath 是相對(duì)路徑(如 upload/xxx.jpg),需拼接 ctx 或 /
            // 假設(shè)你的文件是通過(guò) /common/upload 上傳,且訪問(wèn)路徑為 /upload/...
            // 如果后端返回的是完整 URL,則無(wú)需處理;否則建議統(tǒng)一前綴
            img.src = initialPhotoPath.startsWith('/') ? initialPhotoPath : ctx + initialPhotoPath;
            img.style.maxWidth = '200px';
            img.style.maxHeight = '200px';
            preview.appendChild(img);
            deleteBtn.style.display = 'inline-block';
        }
    }
    initPreview();
    // 文件選擇變化
    fileElem.addEventListener('change', handleFiles);
    // 拖拽事件
    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
        dropArea.addEventListener(eventName, preventDefaults, false);
    });
    function preventDefaults(e) {
        e.preventDefault();
        e.stopPropagation();
    }
    ['dragenter', 'dragover'].forEach(eventName => {
        dropArea.addEventListener(eventName, highlight, false);
    });
    ['dragleave', 'drop'].forEach(eventName => {
        dropArea.addEventListener(eventName, unhighlight, false);
    });
    function highlight() {
        dropArea.style.borderColor = '#007bff';
    }
    function unhighlight() {
        dropArea.style.borderColor = '#ccc';
    }
    dropArea.addEventListener('drop', (e) => {
        preventDefaults(e);
    const files = e.dataTransfer.files;
    handleFiles({ target: { files } });
    });
    function handleFiles(e) {
        const files = e.target.files;
        if (files.length === 0) return;
        selectedFile = files[0];
        if (!selectedFile.type.match('image.*')) {
            alert('請(qǐng)選擇圖片文件(jpg/png/jpeg)');
            selectedFile = null;
            uploadBtn.disabled = true;
            fileElem.value = ''; // 清空,允許重新選擇
            return;
        }
        // 更新預(yù)覽
        preview.innerHTML = '';
        const img = document.createElement('img');
        img.src = URL.createObjectURL(selectedFile);
        img.style.maxWidth = '200px';
        img.style.maxHeight = '200px';
        preview.appendChild(img);
        uploadBtn.disabled = false;
        uploadStatus.textContent = '';
    }
    // 上傳新圖片
    uploadBtn.addEventListener('click', () => {
        if (!selectedFile) return;
    const formData = new FormData();
    formData.append("file", selectedFile);
    // ? 使用 ctx 上下文路徑
    fetch(ctx + 'common/upload', {
        method: 'POST',
        body: formData
    })
        .then(response => response.json())
    .then(data => {
        if (data.code === 0) {
        $('#photoPathHidden').val(data.fileName);
        uploadStatus.textContent = '? 上傳成功';
        uploadBtn.disabled = true;
        deleteBtn.style.display = 'inline-block';
        // 上傳成功后清空 input
        fileElem.value = '';
    } else {
        alert('上傳失敗:' + (data.msg || '未知錯(cuò)誤'));
        uploadStatus.textContent = '? 上傳失敗';
        fileElem.value = ''; // 允許重試
    }
    })
    .catch(err => {
        console.error('上傳出錯(cuò):', err);
    alert('網(wǎng)絡(luò)錯(cuò)誤,請(qǐng)重試');
    uploadStatus.textContent = '? 網(wǎng)絡(luò)錯(cuò)誤';
    fileElem.value = '';
    });
    });
    // 刪除當(dāng)前圖片
    deleteBtn.addEventListener('click', function() {
        if (!confirm('確定要?jiǎng)h除這張照片嗎?刪除后無(wú)法恢復(fù)!')) return;
        const photoPath = $('#photoPathHidden').val();
        if (photoPath) {
            // ? 使用 ctx
            fetch(ctx + 'common/deleteFile?fileName=' + encodeURIComponent(photoPath), {
                method: 'POST'
            })
                .then(response => response.json())
        .then(data => {
                if (data.code !== 0) {
                alert('服務(wù)器刪除失?。? + (data.msg || ''));
            }
            clearPhotoPreview();
        })
        .catch(err => {
                console.error('刪除出錯(cuò):', err);
            alert('網(wǎng)絡(luò)錯(cuò)誤,請(qǐng)重試');
            clearPhotoPreview(); // 仍清除前端
        });
        } else {
            alert('當(dāng)前沒(méi)有照片可刪除');
            clearPhotoPreview();
        }
    });
    function clearPhotoPreview() {
        selectedFile = null;
        $('#photoPathHidden').val('');
        preview.innerHTML = '';
        uploadStatus.textContent = '';
        uploadBtn.disabled = true;
        deleteBtn.style.display = 'none';
        fileElem.value = ''; // 關(guān)鍵:重置文件輸入框
    }
</script>

以上是添加記錄過(guò)程中對(duì)于圖片的上傳與刪除。

下面是刪除數(shù)據(jù)庫(kù)記錄時(shí),同步刪除文件的主要過(guò)程。

/remove控制器

@PostMapping( "/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
    return toAjax(photoRecordService.deletePhotoRecordByIds(ids));
}

/remove方法所對(duì)應(yīng)的services

@Override
public int deletePhotoRecordByIds(String ids) {
    Long[] idArray = Convert.toLongArray(ids);
    // 查詢這些記錄
    List<PhotoRecord> records = photoRecordMapper.selectPhotoRecordByIds(idArray);
    // 刪除文件
    for (PhotoRecord r : records) {
        if (StringUtils.isNotBlank(r.getPhotoPath())) {
            try {
                String realPath = RuoYiConfig.getProfile() +
                        r.getPhotoPath().replaceFirst("^/profile", "");
                Path p = Paths.get(realPath);
                if (Files.exists(p)) Files.delete(p);
            } catch (Exception e) {
                log.warn("批量刪除文件失敗: {}", r.getPhotoPath(), e);
            }
        }
    }
    // 批量刪除數(shù)據(jù)庫(kù)
    return photoRecordMapper.deletePhotoRecordByIds(idArray);
}

mapper

List<PhotoRecord> selectPhotoRecordByIds(@Param("ids") Long[] ids);
int deletePhotoRecordByIds(Long[] ids);

mapper.xml

<select id="selectPhotoRecordByIds" resultType="PhotoRecord" resultMap="PhotoRecordResult">
    SELECT * FROM sys_photo_record
    WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>
<delete id="deletePhotoRecordByIds" parameterType="String">
    delete from sys_photo_record where id in 
    <foreach item="id" collection="array" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

到此這篇關(guān)于Springboot上傳文件與物理刪除的文章就介紹到這了,更多相關(guān)Springboot上傳與刪除內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

尼木县| 札达县| 阿鲁科尔沁旗| 东乌珠穆沁旗| 福贡县| 甘孜县| 丹巴县| 河东区| 武穴市| 海丰县| 洪雅县| 宜兰县| 准格尔旗| 登封市| 汾阳市| 织金县| 射洪县| 盖州市| 南雄市| 民和| 怀仁县| 四川省| 磴口县| 乌兰浩特市| 县级市| 龙南县| 夹江县| 房产| 应用必备| 靖州| 新昌县| 齐河县| 贡嘎县| 大同市| 南昌县| 安新县| 平泉县| 无锡市| 军事| 镇宁| 唐河县|