springboot+ajax上传表单(带有图片)

该博客展示了如何在商品管理系统中实现Ajax图片上传功能。前端使用HTML和模板引擎构建商品列表页面,包括商品分类、添加商品、批量删除等操作。通过引入JS文件,实现了商品列表的渲染和图片上传的AJAX请求。在添加商品时,利用FormData对象读取表单数据,并在后台控制器中处理图片上传和商品插入操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前端(表单)

效果图

在这里插入图片描述
在这里插入图片描述

html 和 模板引擎

html

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/3/15 0015
  Time: 17:39
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>商品列表</title>
    <jsp:include page="/common/backend_common.jsp"/>
    <jsp:include page="/common/page.jsp"/>
    <jsp:include page="/template/product/categoryListTemplate2.jsp"/>
    <jsp:include page="/template/product/productListTemplate.jsp"/>
    <jsp:include page="productForm/productForm.jsp"/>
</head>
<body class="no-skin" style="background: white">

<!-- 内容 -->
<div class="main-content">
    <div class="main-content-inner">
        <!-- 导航路径 -->
        <div class="breadcrumbs ace-save-state" id="breadcrumbs">
            <ul class="breadcrumb">
                <li>
                    <i class="ace-icon fa fa-home home-icon"></i>
                    <a href="/sys/admin/index.page">Home</a>
                </li>
                <li class="">商品管理</li>
                <li class="">商品列表</li>
            </ul><!-- /.breadcrumb -->

            <div class="nav-search" id="nav-search">
                <form class="form-search">
             <span class="input-icon">
                 <input type="text" placeholder="Search ..." class="nav-search-input" id="nav-search-input" autocomplete="off" />
                 <i class="ace-icon fa fa-search nav-search-icon"></i>
             </span>
                    <a href="#">
                        <span class="menu-icon fa fa-shopping-cart" style="margin-left: 30px; color: coral; font-size: 30px"></span>
                    </a>
                </form>
            </div>
        </div>
        <div class="page-content">
            <div class="page-header">
                <h1>商品管理
                    <small>
                        <i class="ace-icon fa fa-angle-double-right"></i>
                        商品列表
                    </small>
                </h1>
            </div><!-- /.page-header -->

            <div class="row">
                <div class="col-xs-12">
                    <div class="col-sm-2">
                        <div class="table-header">
                            商品分类列表&nbsp;&nbsp; <a class="green" href="#"></a>
                        </div>
                        <!-- 读取分类列表 -->
                        <div id="categoryList"></div>

                    </div>
                    <div class="col-xs-10">
                        <ul class="list-inline fa-border">
                            <li><button type="button" class="btn btn-warning fa fa-plus product-add">添加商品</button></li>
                            <li><button type="button" class="btn btn-danger fa fa-trash batchDel-btn">批量删除</button></li>
                        </ul>
                        <div class="col-xs-12 fa-border">
                            <ul class="list-inline">
                                <li>展示&nbsp;&nbsp;<select id="pageSize">
                                    <option value="10">10</option>
                                    <option value="25">25</option>
                                    <option value="50">50</option>
                                    <option value="100">100</option>
                                </select>&nbsp;&nbsp;条记录</li>&nbsp;&nbsp;&nbsp;&nbsp;
                                <li><input type="search" id="keyword" name="keyword" placeholder="关键字"/></li>&nbsp;&nbsp;&nbsp;&nbsp;
                                <li>状态&nbsp;&nbsp;<select id="search_status">
                                    <option value="1">有效</option>
                                    <option value="0">无效</option>
                                </select></li>&nbsp;&nbsp;&nbsp;&nbsp;
                                <li><button class="btn btn-info fa fa-check research">查询</button></li>
                            </ul><!--/.ul-->
                            <table id="simple-table" class="table table-bordered table-hover">
                                <thead>
                                <tr>
                                    <th class="center">
                                        <label>
                                            <input type="checkbox" class="ace  batchDel-th">
                                            <span class="lbl"></span>
                                        </label>
                                    </th>
                                    <th>图片</th>
                                    <th>商品名称</th>
                                    <th>描述</th>
                                    <th>所属分类</th>
                                    <th>价格(元)</th>
                                    <th>创建时间</th>
                                    <th>更新时间</th>
                                    <th>状态</th>
                                    <th>操作</th>
                                </tr>
                                </thead>
                                <tbody id="productList"></tbody>
                            </table>
                            <div class="row" id="productPage"></div>
                        </div>
                    </div>
                </div>
            </div><!-- /.row -->
        </div><!-- /.page-content -->
    </div><!-- /.main-content-inner -->
</div><!-- /.main-content -->


<script src="/js/product/product.js"></script>

</body>
</html>

模板引擎

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/3/26 0026
  Time: 15:45
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div id="dialog-product-form" style="display: none;">
    <form id="productForm" enctype="multipart/form-data">
        <table class="table table-striped table-bordered table-hover dataTable no-footer" role="grid" >
            <tr>
                <td style="width: 80px;"><label for="categorySelectId">所属分类</label></td>
                <td><select id="categorySelectId" name="categoryId"
                            data-placeholder="选择分类" style="width: 150px;"></select></td>
            </tr>
            <tr>
                <td><label for="productName" style="width: 60px">商品名称</label></td>
                <td>
                    <input type="text" name="productName" id="productName" value="" class="text ui-widget-content ui-corner-all">
                    <input type="hidden" name="id" id="productId"/>
                </td>
            </tr>
            <tr>
                <td><label for="productPrice">价格(元)</label></td>
                <td><input type="text" name="price" id="productPrice"
                           value="" class="text ui-widget-content ui-corner-all"></td>
            </tr>
            <tr>
                <td><label for="productImage">商品图片</label></td>
                <td><input type="file" name="productImg" id="productImage" value=""
                           class="text ui-widget-content ui-corner-all" style="width: 200px"></td>
            </tr>
            <tr>
                <td><label for="productStatus">状态</label></td>
                <td>
                    <select id="productStatus" name="productStatus" data-placeholder="状态" style="width: 150px;">
                        <option value="1">有效</option>
                        <option value="0">无效</option>
                    </select>
                </td>
            </tr>
            <tr>
                <td><label for="productDescribes">描述</label></td>
                <td><textarea id="productDescribes" name="describes" class="text ui-widget-content ui-corner-all" rows="3" cols="20" ></textarea></td>
            </tr>
        </table>
    </form>
</div>

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/3/26 0026
  Time: 15:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<script id="productListTemplate" type="x-tmpl-mustache">
{{#productList}}
    <tr role="row" class="product-name odd" data-id="{{id}}"><!--even -->
        <td class="center">
            <label>
                <input type="checkbox" class="ace  batchDel-check">
                <span class="lbl"></span>
            </label>
        </td>
        <td><img src="/img/{{showProductImg}}" width="80px" height="50px"/></td>
        <td>{{productName}}</td>
        <td>{{describes}}</td>
        <td>{{showCategory}}</td>
        <td>{{price}}</td>
        <td>{{#create_time}}{{/create_time}}</td>
        <td>{{#update_time}}{{/update_time}}</td>
        <td>{{#bold}}{{showStatus}}{{/bold}}</td>
        <td>
            <div class="hidden-sm hidden-xs action-buttons">
                <a class="blue product-edit" href="#" data-id="{{id}}">
                    <i class="ace-icon fa fa-pencil bigger-100"></i>
                </a>
                <a class="red product-del" href="#" data-id="{{id}}" data-name="{{name}}">
                    <i class="ace-icon fa fa-trash bigger-100"></i>
                </a>
            </div>
        </td>
    </tr>
{{/productList}}
</script>

引入对应的js文件

显示

function loadProductImgList() {
        $.ajax({
            url : "/product/productImg.json",
            type : "post",
            success : function (result) {
                if (result.status == "success"){
                    // 将每一张图片添加到map中
                    $.each(result.data, function (i, img) {
                        productImgMap[img.id] = img;
                        // alert(img.imgName);
                    });
                }
            }
        });
    }

// 渲染商品列表
function loadProductList(categoryId) {
    loadProductImgList();
    // 获取页码和当前页显示条数
    pageNo = $('#productPage .pageNo').val() || 1;
    pageSize = $('#pageSize').val();
    keyword = $('#keyword').val(); // 获取关键字
    search_status = $('#search_status').val(); // 获取状态
    var url = "/product/product.json?categoryId=" + categoryId; // 请求资源
    $.ajax({
        url: url,
        type: "POST",
        data : {
            pageNo : pageNo,
            pageSize : pageSize,
            keyword : keyword,
            search_status : search_status
        },
        success : function (result) {
            renderProductListAndPage(result, url);
        }
    });
}

// 渲染细节
function renderProductListAndPage(result, url) {
    if (result.status == "success"){
        if (result.data.total > 0){
            var rendered = Mustache.render(productListTemplate, {
                "productList" : result.data.records,
                "showCategory" : function () {
                    return categoryMap[this.categoryId].name;
                },
                "showProductImg" : function () {
                    return productImgMap[this.imgId].imgName;
                },
                "create_time" : function () {
                    return function (text, render){
                        return new Date(
                            this.createTime)
                            .Format("yyyy-MM-dd");
                    }
                },
                "update_time" : function() {
                    return function(text, render) {
                        return new Date(
                            this.updateTime)
                            .Format("yyyy-MM-dd");
                    }
                },
                "showStatus" : function() {
                    return this.productStatus == 1 ? '有效'
                        : (this.productStatus == 0 ? '无效'
                            : '删除');
                },
                "bold" : function() {
                    return function(text, render) {
                        var status = render(text);
                        if (status == '有效') {
                            return "<span class='label label-sm label-success'>有效</span>";
                        } else if (status == '无效') {
                            return "<span class='label label-sm label-warning'>无效</span>";
                        } else {
                            return "<span class='label'>删除</span>";
                        }
                    }
                }
            });
            // 将product添加到map中
            $.each(result.data.records, function (i, product) {
                productMap[product.id] = product;
            });
            //在指定id下添加带数据的页面样式
            $('#productList').html(rendered);
        }else {
            $('#productList').html("");
        }
        // 绑定操作
        bindProductClick();
        pageNo = $('#productPage .pageNo').val() || 1; // 获取当前页码
        pageSize = $('#pageSize').val(); // 获取显示条数
        // 分页页码
        renderPage(
            url,
            result.data.total,
            pageNo,
            pageSize,
            result.data.total > 0 ? result.data.records.length : 0,
            "productPage",
            renderProductListAndPage);
    }else {
        //showMessage("获取分类下的商品列表",result.msg, false);
    }
}

添加

使用ajax上传图片,使用new FormData($(’#productForm’)[0]);读取数据 ,需加入contentType : false, 不使用默认的表头, processData : false, 不序列化表单

 // 添加商品
    $('.product-add').click(function () {
        // 弹出框
        $('#dialog-product-form').dialog({
            model : true, // 背景不可点击
            title : "添加商品",
            open : function (event, ui) {
                $(".ui-dialog").css("width", "350px");//增加模态框的宽高
                // 隐藏关闭按钮
                $('.ui-dialog-titlebar-close', $(this).parent()).hide();
                opcategory();
                // 清空表单
                $('#productForm')[0].reset();
            },
            buttons : {
                "添加" : function (e) {
                    // 阻止默认事件
                    e.preventDefault();
                    // 执行添加分类
                    updateProduct(true, function (data) {
                        // 添加成功后提示信息
                        showMessage("添加商品", data.msg, true);
                        // 关闭模态框
                        $('#dialog-product-form').dialog("close");
                        loadProductList(lastClickCategoryId);
                    }, function (data) {
                        // 添加失败后提示信息
                        showMessage("添加商品", data.msg,false);
                        $('#dialog-product-form').dialog("close");
                    });
                },
                "取消" : function () {
                    $('#dialog-product-form').dialog("close");
                }
            }
        });
    });
    // 执行添加更新操作
function updateProduct(isCreate, successCallback, failCallback) {
    var formData = new FormData($('#productForm')[0]);
    $.ajax({
        url : isCreate ? "/product/insert.json" : "/product/update.json",
        type : "POST",
        data : formData,
        contentType : false,
        processData : false,
        success : function (result) {
            // 数据执行成功返回的消息
            if (result.status == "success"){
                // 带参回调函数
                loadProductList();
                if (successCallback){
                    successCallback(result);
                }
            }else {
                // 执行失败返回的消息
                if (failCallback){
                    failCallback(result);
                }
            }
        }
    });
}

后台

controller

// 返回上商品列表
    @RequestMapping("/product.json")
    @ResponseBody
    public CommonReturnType user(@RequestParam("categoryId") int categoryId, SearchProductVo productVo, PageQuery pageQuery){
        IPage<Product> result = productService.productPageByCategoryId(categoryId, productVo, pageQuery);
        return  CommonReturnType.success(result);
    }

    // 获取图片
    @RequestMapping("/productImg.json")
    @ResponseBody
    public CommonReturnType img(){
        List<ProductImg> result = productService.imgList();
        return CommonReturnType.success(result);
    }


    // 添加商品
    @RequestMapping("/insert.json")
    @ResponseBody
    public CommonReturnType insert(ProductVo productVo) throws IOException {
        productService.insertProduct(productVo);
        return CommonReturnType.success("success");
    }

service

 // 根据分类id查询商品
    @Override
    public IPage<Product> productPageByCategoryId(int categoryId, SearchProductVo productVo, PageQuery pageQuery) {
        // 注解检验
        validator.check(pageQuery);
        // 将vo赋值给dto
        SearchProductDto dto = new SearchProductDto();
        if (StringUtils.isNotBlank(productVo.getKeyword())){
            dto.setKeyword(productVo.getKeyword());
        }
        if (StringUtils.isNotBlank(productVo.getSearch_status())){
            dto.setSearch_status(Integer.parseInt(productVo.getSearch_status()));
        }
        // mybatis-plus 的查询条件
        QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(String.valueOf(categoryId))){
            queryWrapper.eq("category_id", categoryId);
        }
        if (StringUtils.isNotBlank(dto.getKeyword())){
            // 模糊查询 %name%
            queryWrapper.like("product_name", dto.getKeyword());
        }
        if (StringUtils.isNotBlank(String.valueOf(dto.getSearch_status()))){
            queryWrapper.eq("product_status", dto.getSearch_status());
        }
        // 统计符合条件的数据
        int count = productMapper.selectCount(queryWrapper);
        IPage<Product> page = new Page<>(pageQuery.getPageNo(), pageQuery.getPageSize());
        if (count > 0){
            page = productMapper.selectPage(page,queryWrapper.orderByDesc("id"));
            return page;
        }
        return page;
    }

// 添加商品
@Override
public void insertProduct(ProductVo productVo) throws IOException {
     validator.check(productVo);
     System.out.println(productVo.getProductName());
     System.out.println(productVo.getProductImg());

     // 文件名
     String imageName = ImageUtils.uploadImg(productVo.getProductImg());
     System.out.println(imageName);
     if (StringUtils.isNotBlank(imageName)){
         ProductImg img = ProductImg.builder().imgName(imageName).build();
         productImgMapper.insert(img);
     }
     // 获取图片id
     QueryWrapper<ProductImg> queryWrapper = new QueryWrapper<>();
     queryWrapper.eq("img_name", imageName);
     int id = productImgMapper.selectOne(queryWrapper).getId();

    // 商品去重
    if (checkProductNameExist(productVo.getProductName(), productVo.getId())){
        throw new BusinessException("商品已经存在");
    }

    // 建造者模式
    Product entity = Product.builder().productName(productVo.getProductName())
            .describes(productVo.getDescribes()).productStatus(productVo.getProductStatus())
            .categoryId(productVo.getCategoryId()).imgId(id).price(productVo.getPrice())
            .creator("admin").createTime(new Date()).updateTime(new Date()).build();
    // 添加商品
    productMapper.insert(entity);

}

// 查询所有图片
    @Override
    public List<ProductImg> imgList() {
        List<ProductImg> imgs = productImgMapper.selectList(null);
        return imgs;
    }

// 商品去重
private boolean checkProductNameExist(String productName, Integer id) {
    QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("product_name", productName);
    if (null != id){
        queryWrapper.ne("id", id);
    }
    return productMapper.selectCount(queryWrapper) > 0;
}

图片上传自定义地址

package com.lzy.utils;


import org.apache.tomcat.Jar;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.util.ClassUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.jar.JarFile;

public class ImageUtils {

    public static String uploadImg(MultipartFile file) throws IOException {
        // 上传地址
        ApplicationHome applicationHome = new ApplicationHome(ImageUtils.class);
        String path = applicationHome.getSource().getParentFile().getParentFile().getParentFile().getPath()
                + "\\SuperMarket-web\\src\\main\\webapp\\img";
        // 获取图片名
        String fileName = file.getOriginalFilename();
        // 根据时间戳生成新的文件名
        String imageName = System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."));
        // 封装上传文件位置的全路径
        File targetFile  = new File(path,imageName);
        //把本地文件上传到封装上传文件位置的全路径
        file.transferTo(targetFile);

        return imageName;
    }


}
### Spring Boot 集成 Sa-Token 实现认证授权并在前端使用 Vue 的最佳实践 #### 1. 引入依赖 为了使 Spring Boot 应用程序能够利用 Sa-Token 进行安全控制,在 `pom.xml` 文件中引入必要的依赖项[^3]。 ```xml <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>${latest.version}</version> </dependency> ``` #### 2. 启动类配置 确保启动类上有 `@EnableSaTokenSsoServer` 注解,这有助于激活单点登录服务端功能。对于大多数应用场景而言,可能不需要此注解;但是如果有计划扩展至分布式会话管理,则建议提前做好准备。 #### 3. 组件注册 创建自定义的控制器或其他业务逻辑组件时,务必记得在其上添加 `@Component` 或者其他合适的 Spring Bean 注解,以便这些对象可以被自动扫描并加入到 IoC 容器之中,从而顺利地参与到 Sa-Token 的工作流程里来[^2]。 #### 4. 认证拦截设置 通过重写默认的安全过滤链或编写特定路径下的访问规则,可以在请求到达目标资源之前对其进行身份验证处理。例如: ```java import cn.dev33.satoken.interceptor.SaInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SecurityConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 添加 Sa-Token 拦截器, 开启注解式鉴权 registry.addInterceptor(new SaInterceptor()) .addPathPatterns("/**"); } } ``` 以上代码片段展示了如何全局应用 Sa-Token 提供的身份验证机制于所有的 HTTP 请求之上。 #### 5. 前端集成 (Vue.js) 在基于 Vue 构建的应用界面侧,通常的做法是在发起 API 调用前附加 token 到 headers 中作为凭证传递给后端服务器进行校验。这里给出一个简单的 Axios 插件实例化方式用于简化这一过程: ```javascript // main.js or wherever you initialize axios instance import axios from 'axios'; const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, }); service.interceptors.request.use( config => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }, error => Promise.reject(error), ); ``` 上述 JavaScript 片段说明了怎样借助本地存储中的 token 字符串构建带有适当头部信息的 AJAX 请求发送出去[^1]。 #### 6. 用户登录接口设计 最后但同样重要的是,应该有一个清晰明了的方式让用户完成登录操作并将获得的有效期较短的一次性令牌存放在客户端环境中以备后续调用所需的服务API所用。下面是一个 RESTful 风格 POST 方法的例子用来接收用户名密码参数返回 JSON 结构化的响应数据包含新生成好的 access_token : ```json POST /login HTTP/1.1 Host: localhost:8080 Content-Type: application/json;charset=UTF-8 Cache-Control: no-cache { "username": "admin", "password": "123" } HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 { "code": 200, "msg": "", "data": { "access_token": "eyJhbGciOiJIUzI1NiJ9..." } } ``` 这种交互模式不仅适用于传统的表单提交场景也兼容现代 SPA(Single Page Application)架构下异步加载页面内容的需求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值