java 后端练习作业——前后端分离及文件保存
·
本篇将先前的表格输出修改为前后端分离架构,前端采用React框架实现,IDE使用VSCode。
React前端代码
在web项目同一目录下创建新的文件夹命名为WebReact。
在终端cd到WebReact目录下执行代码创建出React框架。
在index.js里配置路由:
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import ReactDOM from 'react-dom/client';
import locale from 'antd/locale/zh_CN';
import { ConfigProvider } from 'antd';
import App from './App.js';
const sys = React.sys;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
//不能加这个StrictMode,否则组件的componentDidMount会执行两次
// <React.StrictMode>
<BrowserRouter>
{/* <LocaleProvider locale={zh_CN}> */}
<ConfigProvider locale={locale}>
<Routes>
<Route key="App" path="/*" element={<App />} />
<Route key="fileupload" path="fileupload/*" element={React.createElement(require("./FileUpload").default)} />
</Routes>
{/* <Login /> */}
</ConfigProvider>
{/* </LocaleProvider> */}
</BrowserRouter>
// </React.StrictMode>
);
这里App.js用于显示表格,代码如下:
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [], // 存放后端返回的数据
loading: true, // 加载状态
error: null // 错误信息
};
}
// 生命周期钩子:组件挂载后调用后端接口
componentDidMount() {
// 后端 Servlet 返回 JSON 的接口地址(请改成你自己的项目路径)
const apiUrl = "http://localhost:8080/MyWeb/api/products";
fetch(apiUrl)
.then((response) => {
if (!response.ok) {
throw new Error("网络响应失败: " + response.status);
}
return response.json();
})
.then((data) => {
this.setState({ products: data, loading: false });
})
.catch((error) => {
console.error("请求出错:", error);
this.setState({ error, loading: false });
});
}
render() {
const { products, loading, error } = this.state;
if (loading) {
return <p style={{ padding: "20px" }}>正在加载数据,请稍候...</p>;
}
if (error) {
return (
<p style={{ padding: "20px", color: "red" }}>
数据加载失败:{error.message}
</p>
);
}
return (
<div style={{ padding: "30px", fontFamily: "Arial" }}>
<h2>商品信息列表</h2>
<table
border="1"
cellPadding="8"
style={{ borderCollapse: "collapse", width: "100%" }}
>
<thead style={{ backgroundColor: "#f5f5f5" }}>
<tr>
<th>商品编号</th>
<th>品牌</th>
<th>型号</th>
<th>价格(元)</th>
</tr>
</thead>
<tbody>
{products.map((p, index) => (
<tr key={index}>
<td>{p.pid}</td>
<td>{p.brand}</td>
<td>{p.model}</td>
<td>{p.price}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
FileUpload.js用于文件上传,代码如下:
import React, { Component } from "react";
class FileUpload extends Component {
constructor(props) {
super(props);
this.state = {
selectedFile: null,
uploading: false,
message: "",
};
}
handleFileChange = (e) => {
this.setState({ selectedFile: e.target.files[0], message: "" });
};
handleUpload = (e) => {
e.preventDefault();
const { selectedFile } = this.state;
if (!selectedFile) {
this.setState({ message: "⚠️ 请先选择文件!" });
return;
}
const formData = new FormData();
formData.append("myfile", selectedFile);
this.setState({ uploading: true, message: "正在上传,请稍候..." });
fetch("http://localhost:8080/MyWeb/upload", {
method: "POST",
body: formData,
})
.then((res) => {
if (!res.ok) {
throw new Error("状态码:" + res.status);
}
return res.json(); // ✅ 改成 JSON
})
.then((data) => {
if (data.success) {
this.setState({
uploading: false,
message: `上传成功!文件名:${data.fileName}`,
});
} else {
this.setState({
uploading: false,
message: `上传失败:${data.message}`,
});
}
})
.catch((err) => {
this.setState({
uploading: false,
message: "上传出错:" + err.message,
});
});
};
render() {
const { selectedFile, message, uploading } = this.state;
return (
<div style={{ padding: "40px", fontFamily: "Arial" }}>
<h2>📂 文件上传系统</h2>
<form onSubmit={this.handleUpload}>
<input type="file" onChange={this.handleFileChange} />
<br />
<br />
<button
type="submit"
disabled={uploading}
style={{
padding: "8px 16px",
backgroundColor: uploading ? "#999" : "#007bff",
color: "#fff",
border: "none",
borderRadius: "4px",
cursor: uploading ? "not-allowed" : "pointer",
}}
>
{uploading ? "正在上传..." : "上传文件"}
</button>
</form>
{selectedFile && (
<p style={{ marginTop: "15px" }}>
已选择文件:<strong>{selectedFile.name}</strong>
</p>
)}
{message && (
<p
style={{
marginTop: "20px",
color: message.includes("✅") ? "green" : "red",
fontWeight: "bold",
}}
>
{message}
</p>
)}
</div>
);
}
}
export default FileUpload;
Servlet后端代码
后端productsInfo的servlet如下:
package MyWork.chp03.servlet;
import MyWork.chp03.entity.Product;
import MyWork.chp03.dao.ProductDao;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.List;
// 建议统一小写命名,接口路径改为 /api/products 以符合REST风格
@WebServlet(name = "productsInfo", value = "/api/products")
public class productsInfo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ===== 允许跨域 (React 前端默认端口是3000) =====
response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
// ===== 查询数据库 =====
ProductDao productDao = new ProductDao();
List<Product> products = productDao.selectAll();
// ===== 转换为 JSON 格式 =====
// 你可以使用简单的手工拼接(或用 Gson)
StringBuilder json = new StringBuilder("[");
for (int i = 0; i < products.size(); i++) {
Product p = products.get(i);
json.append("{")
.append("\"pid\":\"").append(p.getPid()).append("\",")
.append("\"brand\":\"").append(p.getBrand()).append("\",")
.append("\"model\":\"").append(p.getModel()).append("\",")
.append("\"price\":").append(p.getPrice())
.append("}");
if (i < products.size() - 1) {
json.append(",");
}
}
json.append("]");
// ===== 输出 JSON 给前端 =====
out.print(json.toString());
out.flush();
} catch (SQLException e) {
e.printStackTrace();
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
out.print("{\"error\": \"Database query failed.\"}");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
后端ExcelServlet的servlet如下:
package MyWork.chp03.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
@WebServlet("/excelDemo")
public class ExcelServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=data.xls");
PrintWriter out = response.getWriter();
out.println("编号\t姓名\t成绩");
out.println("1\t张三\t90");
out.println("2\t李四\t85");
out.println("3\t王五\t88");
out.flush();
out.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
前端表格界面
前端上传文件界面

更多推荐



所有评论(0)