diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/config/WebConfig.java b/src/main/java/uk/ac/cf/spring/demo/sports/config/WebConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..4bd1f4302b749e201d9d8f90819e33f9ae8043ec
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/config/WebConfig.java
@@ -0,0 +1,14 @@
+package uk.ac.cf.spring.demo.sports.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("/uploads/**")
+                .addResourceLocations("file:./uploads/"); // 指定静态资源的实际路径
+    }
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageController.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageController.java
new file mode 100644
index 0000000000000000000000000000000000000000..c137de20530d933d88f72b4ff075e905d877e7ff
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageController.java
@@ -0,0 +1,39 @@
+package uk.ac.cf.spring.demo.sports.images;
+import java.io.IOException;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+@RequestMapping("/api/images")
+public class ImageController {
+    private final ImageService imageService;
+
+    @Autowired
+    public ImageController(ImageService imageService) {
+        this.imageService = imageService;
+    }
+
+    @GetMapping
+    public List<Image> getImages() {
+        List<Image> images = imageService.getAllImages();
+        System.out.println("Returned JSON data: " + images);
+        return imageService.getAllImages();
+    }
+
+    @PostMapping
+    public ResponseEntity<Void> uploadImage(@RequestParam("image") MultipartFile file) {
+        try {
+            imageService.saveImage(file);
+            List<Image> images = imageService.getAllImages();
+            System.out.println("Returned JSON data: " + images);
+            return ResponseEntity.ok().build();
+        } catch (IOException e) {
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
+        }
+    }
+}
+
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageRepository.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..3feedb21d13c98ddd40abc5592b4e2436dc5e50c
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageRepository.java
@@ -0,0 +1,42 @@
+package uk.ac.cf.spring.demo.sports.images;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+@Repository
+public class ImageRepository {
+    private final JdbcTemplate jdbcTemplate;
+
+    @Autowired
+    public ImageRepository(JdbcTemplate jdbcTemplate) {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+
+    public int save(String url) {
+        String sql = "INSERT INTO images (url) VALUES (?)";
+        return jdbcTemplate.update(sql, url);
+    }
+    // 获取所有图片记录
+    public List<Image> findAll() {
+        String sql = "SELECT * FROM images";
+        return jdbcTemplate.query(sql, new ImageRowMapper());
+    }
+    //映射方法
+    private static class ImageRowMapper implements RowMapper<Image> {
+        @Override
+        public Image mapRow(ResultSet rs, int rowNum) throws SQLException {
+            Image image = new Image();
+            image.setId(rs.getInt("id"));
+            image.setUrl(rs.getString("url"));
+            image.setUploadedAt(rs.getTimestamp("uploaded_at"));
+            return image;
+        }
+    }
+
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageService.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageService.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ea853716315f773b269a6b96fb220e54dbc5f19
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageService.java
@@ -0,0 +1,11 @@
+package uk.ac.cf.spring.demo.sports.images;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.List;
+
+public interface ImageService {
+    List<Image> getAllImages();
+    void saveImage(MultipartFile file) throws IOException;
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageServiceImpl.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..bac30bb083bd67189a56cfbd86f3fd4bb8373572
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageServiceImpl.java
@@ -0,0 +1,50 @@
+package uk.ac.cf.spring.demo.sports.images;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.UUID;
+
+@Service
+public class ImageServiceImpl implements ImageService {
+
+    private final ImageRepository imageRepository;
+
+    @Value("${image.upload.dir}")
+    private String uploadDir;
+
+    @Autowired
+    public ImageServiceImpl(ImageRepository imageRepository) {
+        this.imageRepository = imageRepository;
+    }
+
+    @Override
+    public List<Image> getAllImages() {
+        return imageRepository.findAll();
+    }
+
+    @Override
+    public void saveImage(MultipartFile file) throws IOException {
+        // 生成唯一文件名
+        String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
+        Path filePath = Paths.get(uploadDir, fileName);
+
+        // 确保目录存在
+        Files.createDirectories(filePath.getParent());
+
+        // 将文件写入目标目录
+        Files.write(filePath, file.getBytes());
+
+        // 保存图片路径到数据库
+        String url = "/uploads/" + fileName;
+        imageRepository.save(url);
+    }
+
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
index 77c1947120a65521f76bcece1485890c00989625..e401b0ff6a5f35c377ab513f4f56e65afceddf82 100644
--- a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
@@ -31,6 +31,8 @@ public class SecurityConfig {
             "/html/login.html" , // login
             "/rankings",          //ranking data path
             "/rankings/**",
+            "/uploads",            //images
+            "/uploads/**",
 
     };
 
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index a025cc6041e1be9ce644d6cdb23ec401ff2e70e6..59586e3f5bac34c5411ad3b7cfa4e13e2aebf1e4 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -9,4 +9,5 @@ spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
 #spring.mvc.view.suffix=.html                  # ????
 spring.thymeleaf.prefix=classpath:/templates/
 spring.thymeleaf.suffix=.html
-spring.thymeleaf.mode=HTML
\ No newline at end of file
+spring.thymeleaf.mode=HTML
+image.upload.dir=./uploads
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index 4e0ea9ec9070dc0cd41f4bb326cbf4ecf9d64ad5..32ef80c21843a90af4ef0ae5c8aed599fcf28c77 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -40,7 +40,6 @@ CREATE TABLE IF NOT EXISTS ranking (
 );
 CREATE TABLE images (
                         id INT AUTO_INCREMENT PRIMARY KEY,
-                        filename VARCHAR(255) NOT NULL,  -- 图片文件名或路径
-                        uploader VARCHAR(100) NOT NULL,  -- 上传者名称
-                        upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 上传时间
+                        url VARCHAR(255) NOT NULL,  -- 图片文件名或路径
+                        uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP-- 上传时间
 );
diff --git a/src/main/resources/static/css/gallery.css b/src/main/resources/static/css/gallery.css
new file mode 100644
index 0000000000000000000000000000000000000000..0404d3e5d594bcdb5f44fe1ba4397b4f29cf5a9e
--- /dev/null
+++ b/src/main/resources/static/css/gallery.css
@@ -0,0 +1,89 @@
+/* General Styles for Gallery */
+.gallery-container {
+    max-width: 1200px;
+    margin: 20px auto;
+    padding: 20px;
+    text-align: center;
+    font-family: Arial, sans-serif;
+    background-color: #f9f9f9;
+    border-radius: 8px;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.gallery-container h1 {
+    margin-bottom: 20px;
+    font-size: 2em;
+    color: #333;
+}
+
+/* Carousel Styles */
+.carousel {
+    position: relative;
+    overflow: hidden;
+    width: 80%;
+    margin: 0 auto;
+    max-height: 400px;
+}
+
+#carousel-images {
+    display: flex;
+    transition: transform 0.5s ease-in-out;
+}
+
+#carousel-images img {
+    max-width: 100%;
+    max-height: 400px;
+    object-fit: cover;
+    border-radius: 8px;
+}
+
+.carousel-button {
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    background-color: rgba(0, 0, 0, 0.5);
+    color: white;
+    border: none;
+    padding: 10px;
+    cursor: pointer;
+    border-radius: 50%;
+    font-size: 1.5em;
+}
+
+.carousel-button.prev {
+    left: 10px;
+}
+
+.carousel-button.next {
+    right: 10px;
+}
+
+.carousel-button:hover {
+    background-color: rgba(0, 0, 0, 0.8);
+}
+
+/* Upload Section */
+.upload-section {
+    margin-top: 20px;
+}
+
+.upload-btn {
+    background-color: #007bff;
+    color: white;
+    border: none;
+    padding: 10px 20px;
+    font-size: 1em;
+    border-radius: 4px;
+    cursor: pointer;
+    transition: background-color 0.3s ease;
+}
+
+.upload-btn:hover {
+    background-color: #0056b3;
+}
+.carousel img {
+    display: block;          /* Ensure the image displays as a block-level element */
+    margin: 0 auto;          /* Horizontally centers the image */
+    max-width: 100%;         /* Keep image responsive */
+    height: auto;
+}
\ No newline at end of file
diff --git a/src/main/resources/static/html/gallery.html b/src/main/resources/static/html/gallery.html
new file mode 100644
index 0000000000000000000000000000000000000000..71a86f2db25eec323ba3cd86bcc15170d7d08dbd
--- /dev/null
+++ b/src/main/resources/static/html/gallery.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>image gallery</title>
+    <link rel="stylesheet" href="/css/navBar.css">
+    <link rel="stylesheet" href="/css/footer.css">
+    <link rel="stylesheet" href="/css/gallery.css">
+</head>
+<body>
+<div id="navbar-container"></div>
+
+<!-- Main Content -->
+<main class="gallery-container">
+    <h1>Image Gallery</h1>
+
+    <!-- Carousel -->
+    <div class="carousel">
+        <div id="carousel-images">
+            <!-- Images will be dynamically inserted here -->
+        </div>
+        <button class="carousel-button prev" onclick="prevImage()">&#10094;</button>
+        <button class="carousel-button next" onclick="nextImage()">&#10095;</button>
+    </div>
+
+    <!-- Upload Section -->
+    <div class="upload-section">
+        <button class="upload-btn" onclick="document.getElementById('file-input').click()">Upload Image</button>
+        <input type="file" id="file-input" accept="image/*" style="display: none;" onchange="uploadImage(event)">
+    </div>
+</main>
+
+<!-- Footer Section -->
+<div id="footer-container"></div>
+
+<!-- JavaScript -->
+<script src="/js/navBar.js"></script>
+<script src="/js/footer.js"></script>
+<script src="/js/gallery.js"></script>
+<script>
+    // Load header and footer
+    loadNavbar('navbar-container');
+    loadFooter('footer-container');
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/static/html/ranking.html b/src/main/resources/static/html/ranking.html
index c9ff9753da94074d46005e75bb706310c07c6d9c..691ed42b53246422fa502a51bbbd800363ca12f8 100644
--- a/src/main/resources/static/html/ranking.html
+++ b/src/main/resources/static/html/ranking.html
@@ -52,6 +52,5 @@
   loadFooter('footer-container');
 </script>
 <script src="/js/rankingTable.js"></script>
-<script src="/js/matchSchedule.js"></script>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/main/resources/static/js/gallery.js b/src/main/resources/static/js/gallery.js
new file mode 100644
index 0000000000000000000000000000000000000000..68949a4ed82cabdaf0cfb421cc27e5db464190d6
--- /dev/null
+++ b/src/main/resources/static/js/gallery.js
@@ -0,0 +1,70 @@
+let currentIndex = 0;
+let images = []; // This will hold the URLs of the images
+
+// Load images into the carousel
+function loadImages() {
+    const carouselImages = document.getElementById("carousel-images");
+    carouselImages.innerHTML = ""; // Clear current images
+
+    images.forEach((imageUrl, index) => {
+        const img = document.createElement("img");
+        img.src = imageUrl; // Correctly use the URL for the image src
+        console.log(img.src)
+        img.alt = `Image ${index + 1}`;
+        img.style.display = index === currentIndex ? "block" : "none";
+        carouselImages.appendChild(img);
+    });
+}
+
+// Show previous image
+function prevImage() {
+    currentIndex = (currentIndex - 1 + images.length) % images.length;
+    loadImages();
+}
+
+// Show next image
+function nextImage() {
+    currentIndex = (currentIndex + 1) % images.length;
+    loadImages();
+}
+
+// Upload image
+function uploadImage(event) {
+    const file = event.target.files[0];
+    if (!file) return;
+
+    const formData = new FormData();
+    formData.append("image", file);
+
+    fetch("/api/images", {
+        method: "POST",
+        body: formData,
+    })
+        .then(response => {
+            if (!response.ok) {
+                throw new Error("Failed to upload image");
+            }
+            return response.json();
+        })
+        .then(data => {
+            images.push(data.url); // Assuming the server returns the image URL
+            loadImages();
+        })
+        .catch(error => {
+            console.error("Error uploading image:", error);
+        });
+}
+
+// Initialize the gallery on page load
+window.onload = () => {
+    fetch("/api/images")
+        .then(response => response.json())
+        .then(data => {
+            images = data.map(image => image.url); // Extract URLs from the server response
+            loadImages();
+        })
+        .catch(error => {
+            console.error("Error loading images:", error);
+        });
+};
+