feat: expand struts demo lab

This commit is contained in:
Codex
2026-03-18 18:12:20 +08:00
parent 1f60832445
commit fba7b0497f
25 changed files with 1847 additions and 1447 deletions

View File

@@ -1,102 +1,50 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传 - Struts2 学习</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Guide</title>
<style>
body { font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #667eea, #764ba2); min-height: 100vh; margin: 0; padding: 20px; }
.container { max-width: 1200px; margin: 0 auto; }
.breadcrumb { background: white; padding: 15px 25px; border-radius: 10px; margin-bottom: 20px; }
.content { background: white; border-radius: 20px; padding: 40px; }
h1 { color: #667eea; border-bottom: 3px solid #667eea; padding-bottom: 15px; }
h2 { color: #764ba2; margin-top: 30px; }
.section { margin: 25px 0; padding: 20px; background: #f8f9fa; border-radius: 10px; }
pre { background: #1e1e1e; color: #d4d4d4; padding: 20px; border-radius: 10px; overflow-x: auto; }
.keyword { color: #569cd6; }
.string { color: #ce9178; }
.comment { color: #6a9955; }
.btn { display: inline-block; padding: 12px 30px; background: #667eea; color: white; text-decoration: none; border-radius: 25px; }
body { margin: 0; padding: 24px; font-family: "Aptos", "Segoe UI", sans-serif; background: linear-gradient(135deg, #0ea5e9, #38bdf8); }
.shell { max-width: 980px; margin: 0 auto; background: rgba(255,255,255,0.96); border-radius: 28px; padding: 28px; box-shadow: 0 24px 60px rgba(0,0,0,0.18); }
.eyebrow { font-size: 12px; text-transform: uppercase; letter-spacing: 0.12em; color: #0284c7; font-weight: 800; }
h1, h2 { margin: 10px 0 12px; }
p, li { color: #53667d; line-height: 1.9; }
pre { background: #101827; color: #d9e7ff; padding: 18px; border-radius: 18px; overflow-x: auto; }
.links { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 18px; }
.btn { display: inline-flex; padding: 10px 14px; border-radius: 999px; text-decoration: none; font-weight: 700; background: #edf7ff; color: #0284c7; }
</style>
</head>
<body>
<div class="container">
<div class="breadcrumb">
<a href="/">🏠 首页</a> / 文件上传
</div>
<div class="content">
<h1>📁 文件上传</h1>
<h2>1. 单文件上传</h2>
<div class="section">
<pre><span class="comment">// Action</span>
<span class="keyword">public class</span> UploadAction <span class="keyword">extends</span> ActionSupport {
<span class="comment">// 文件对象</span>
<span class="keyword">private</span> File upload;
<span class="comment">// 文件名 (格式: 属性名FileName)</span>
<span class="keyword">private</span> String uploadFileName;
<span class="comment">// 文件类型 (格式: 属性名ContentType)</span>
<span class="keyword">private</span> String uploadContentType;
<span class="annotation">@Override</span>
<span class="keyword">public</span> String execute() <span class="keyword">throws</span> Exception {
<span class="comment">// 保存文件</span>
String path = ServletActionContext.getServletContext()
.getRealPath(<span class="string">"/upload"</span>);
<span class="keyword">new</span> File(path, uploadFileName).createNewFile();
<span class="comment">// 复制文件</span>
FileUtils.copyFile(upload, <span class="keyword">new</span> File(path, uploadFileName));
<span class="keyword">return</span> SUCCESS;
}
<span class="comment">// getter/setter</span>
<span class="keyword">public</span> File getUpload() { <span class="keyword">return</span> upload; }
<span class="keyword">public void</span> setUpload(File upload) { <span class="keyword">this</span>.upload = upload; }
<span class="keyword">public</span> String getUploadFileName() { <span class="keyword">return</span> uploadFileName; }
<span class="keyword">public void</span> setUploadFileName(String uploadFileName) { <span class="keyword">this</span>.uploadFileName = uploadFileName; }
<span class="keyword">public</span> String getUploadContentType() { <span class="keyword">return</span> uploadContentType; }
<span class="keyword">public void</span> setUploadContentType(String uploadContentType) { <span class="keyword">this</span>.uploadContentType = uploadContentType; }
}</pre>
</div>
<h2>2. JSP 表单</h2>
<div class="section">
<pre><span class="comment">&lt;%@ taglib prefix="s" uri="/struts-tags" %&gt;</span>
<span class="keyword">&lt;s:form</span> action=<span class="string">"upload"</span> method=<span class="string">"post"</span> enctype=<span class="string">"multipart/form-data"</span><span class="keyword">&gt;</span>
<span class="keyword">&lt;s:file</span> name=<span class="string">"upload"</span> label=<span class="string">"选择文件"</span>/&gt;
<span class="keyword">&lt;s:submit</span> value=<span class="string">"上传"</span>/&gt;
<span class="keyword">&lt;/s:form&gt;</span></pre>
</div>
<h2>3. 多文件上传</h2>
<div class="section">
<pre><span class="comment">// 使用 List 接收多文件</span>
<span class="keyword">private</span> List&lt;File&gt; uploads;
<span class="keyword">private</span> List&lt;String&gt; uploadsFileName;
<div class="shell">
<div class="eyebrow">Guide</div>
<h1>Upload demo guide</h1>
<p>The refreshed upload flow is deliberately safer for demo use. It shows multipart binding and metadata capture without persisting files.</p>
<span class="comment">// 表单</span>
<span class="keyword">&lt;s:file</span> name=<span class="string">"uploads"</span> multiple=<span class="string">"multiple"</span>/&gt;</pre>
</div>
<h2>4. 限制文件大小和类型</h2>
<div class="section">
<pre><span class="comment">// struts.xml 配置</span>
<span class="keyword">&lt;action</span> name=<span class="string">"upload"</span> class=<span class="string">"com.demo.UploadAction"</span><span class="keyword">&gt;</span>
<span class="keyword">&lt;interceptor-ref</span> name=<span class="string">"fileUpload"</span><span class="keyword">&gt;</span>
<span class="keyword">&lt;param</span> name=<span class="string">"maximumSize"</span><span class="keyword">&gt;</span>10485760<span class="keyword">&lt;/param&gt;</span><span class="comment">&lt;!-- 10MB --&gt;</span>
<span class="keyword">&lt;param</span> name=<span class="string">"allowedTypes"</span><span class="keyword">&gt;</span>image/png,image/jpeg<span class="keyword">&lt;/param&gt;</span>
<span class="keyword">&lt;/interceptor-ref&gt;</span>
<span class="keyword">&lt;result&gt;</span>/upload/success.jsp<span class="keyword">&lt;/result&gt;</span>
<span class="keyword">&lt;/action&gt;</span></pre>
</div>
<a href="/demo/ajax" class="btn">下一节AJAX →</a>
<h2>Core binding fields</h2>
<ul>
<li><code>File upload</code> for the primary file object.</li>
<li><code>String uploadFileName</code> for the original filename.</li>
<li><code>String uploadContentType</code> for the MIME type.</li>
<li>Optional lists for multiple files.</li>
</ul>
<pre>public class FileUploadAction extends ActionSupport {
private File upload;
private String uploadFileName;
private String uploadContentType;
public String execute() {
// demo keeps metadata only
return SUCCESS;
}
}</pre>
<div class="links">
<a class="btn" href="../../upload/index.jsp">Open upload demo</a>
<a class="btn" href="../../index.jsp">Back to portal</a>
</div>
</div>
</body>
</html>
</html>