首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Struts2 文件上傳完整實戰:客戶端校驗、服務端控制、圖片預覽與壓縮

     編輯:Java 相關知識

依賴準備

<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-core</artifactId>
    <version>2.5.30</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

Struts2 文件上傳約定

Action 需提供三個屬性(以 myFile 為例):

屬性類型說明
myFileFile上傳的臨時文件
myFileFileNameString原始文件名
myFileContentTypeStringMIME 類型

前端頁面:上傳表單 + 即時預覽 + 類型校驗

<form id="uploadForm" action="upload.action" method="post" enctype="multipart/form-data">
    <input type="file" id="fileInput" name="myFile" accept="image/*" />
    <button type="submit" id="submitBtn" disabled>上傳</button>
    <img id="preview" src="#" alt="預覽" style="display:none; max-width:200px;" />
    <div id="errorMsg" style="color:red; display:none;"></div>
</form>

<script>
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif'];
const MAX_SIZE = 2 * 1024 * 1024; // 2MB

const fileInput = document.getElementById('fileInput');
const preview = document.getElementById('preview');
const submitBtn = document.getElementById('submitBtn');
const errorMsg = document.getElementById('errorMsg');

fileInput.addEventListener('change', function() {
    const file = this.files[0];
    errorMsg.style.display = 'none';
    preview.style.display = 'none';
    submitBtn.disabled = true;

    if (!file) return;

    // 1. 類型校驗
    if (!ALLOWED_TYPES.includes(file.type)) {
        showError('僅支持 JPG/PNG/GIF 格式');
        this.value = ''; // 清空選擇
        return;
    }

    // 2. 大小校驗
    if (file.size > MAX_SIZE) {
        showError('文件大小不能超過 2MB');
        this.value = '';
        return;
    }

    // 3. 生成預覽 (FileReader)
    const reader = new FileReader();
    reader.onload = function(e) {
        preview.src = e.target.result;
        preview.style.display = 'block';
        submitBtn.disabled = false;
    };
    reader.readAsDataURL(file);
});

function showError(msg) {
    errorMsg.textContent = msg;
    errorMsg.style.display = 'block';
}
</script>

struts.xml 配置:攔截器參數化控制

<package name="upload" extends="struts-default">
    <action name="upload" class="com.example.UploadAction">
        <interceptor-ref name="defaultStack">
            <param name="fileUpload.allowedTypes">image/jpeg,image/png,image/gif</param>
            <param name="fileUpload.maximumSize">2097152</param> <!-- 2MB -->
        </interceptor-ref>
        <result name="success">/upload/success.jsp</result>
        <result name="input">/upload/upload.jsp</result> <!-- 校驗失敗返回 -->
    </action>
</package>

國際化錯誤消息:globalMessage_zh_CN.properties

struts.messages.error.file.too.large=文件大小超過限制 ({1} bytes)
struts.messages.error.content.type.not.allowed=文件類型不允許: {1}
struts.messages.error.uploading=上傳失敗: {1}

Action 類:接收文件、壓縮存儲、返回結果

@Controller
public class UploadAction extends ActionSupport {
    private File myFile;
    private String myFileFileName;
    private String myFileContentType;

    // 業務常量
    private static final String UPLOAD_DIR = "/var/www/uploads";
    private static final int MAX_WIDTH = 800;
    private static final int MAX_HEIGHT = 600;
    private static final float QUALITY = 0.8f;

    @Override
    public String execute() {
        if (myFile == null) {
            addActionError(getText("struts.messages.error.uploading", new String[]{"未選擇文件"}));
            return INPUT;
        }

        try {
            // 1. 確保目錄存在
            File dir = new File(UPLOAD_DIR);
            if (!dir.exists()) dir.mkdirs();

            // 2. 生成唯一文件名 (UUID + 原始擴展名)
            String ext = FilenameUtils.getExtension(myFileFileName);
            String newFileName = UUID.randomUUID().toString() + "." + ext;
            File destFile = new File(dir, newFileName);

            // 3. 圖片壓縮 + 存儲
            compressAndSave(myFile, destFile);

            // 4. 可選:記錄數據庫、返回文件 URL 等
            addActionMessage("上傳成功: " + newFileName);
            return SUCCESS;
        } catch (Exception e) {
            log.error("上傳處理異常", e);
            addActionError(getText("struts.messages.error.uploading", new String[]{e.getMessage()}));
            return INPUT;
        }
    }

    /** 使用 ImageIO + ImageWriter 進行質量壓縮 */
    private void compressAndSave(File src, File dest) throws IOException {
        BufferedImage original = ImageIO.read(src);
        if (original == null) throw new IOException("非有效圖片文件");

        // 計算縮放比例
        int w = original.getWidth();
        int h = original.getHeight();
        double scale = Math.min((double) MAX_WIDTH / w, (double) MAX_HEIGHT / h);
        if (scale >= 1.0) {
            // 無需縮放,直接複製
            Files.copy(src.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
            return;
        }

        int newW = (int) (w * scale);
        int newH = (int) (h * scale);
        BufferedImage resized = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = resized.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.drawImage(original, 0, 0, newW, newH, null);
        g2d.dispose();

        // JPEG 壓縮質量控制
        ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
        ImageWriteParam param = writer.getDefaultWriteParam();
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(QUALITY);

        try (ImageOutputStream ios = ImageIO.createImageOutputStream(dest)) {
            writer.setOutput(ios);
            writer.write(null, new IIOImage(resized, null, null), param);
        } finally {
            writer.dispose();
        }
    }

    // Getter/Setter 省略
    public void setMyFile(File myFile) { this.myFile = myFile; }
    public void setMyFileFileName(String myFileFileName) { this.myFileFileName = myFileFileName; }
    public void setMyFileContentType(String myFileContentType) { this.myFileContentType = myFileContentType; }
}

服務端校驗效果對比

場景前端攔截後端攔截 (fileUpload 攔截器)
未選文件點提交按鈕禁用Action 執行,myFile 為 null,返回 INPUT
選擇 .exe 文件彈窗提示、清空輸入返回 INPUT,顯示「文件類型不允許」
選擇 5MB 圖片彈窗提示、清空輸入返回 INPUT,顯示「文件大小超過限制」
正確圖片顯示預覽、啟用按鈕執行 execute(),壓縮存儲,返回 SUCCESS

總結

Struts2 文件上傳最佳實踐:

  1. 雙重防護:前端即時反饋體驗好,後端攔截器兜底安全
  2. 參數化配置:struts.xml 中聲明類型/大小限制,無硬編碼
  3. 國際化提示:globalMessage.properties 統一錯誤文案
  4. 圖片處理:壓縮存儲節省空間,UUID 命名避免衝突
  5. 資源清理:ImageWriter.dispose()、流關閉防洩漏
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved