IDEA阿里云OSS实现文件上传·解决苍穹外卖图片回显

简单交代配置阿里云OSS的思路

1. 首先去阿里云开通一个OSS服务,配置好一个自己的Bucket

2. 在IDEA配置Bucket

3. 拷贝官网的OSS工具类代码

package com.sky.utils;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;

@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;

    /**
     * 文件上传
     *
     * @param bytes
     * @param objectName
     * @return
     */
    public String upload(byte[] bytes, String objectName) {

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 创建PutObject请求。
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }

        //文件访问路径规则 https://BucketName.Endpoint/ObjectName
        StringBuilder stringBuilder = new StringBuilder("https://");
        stringBuilder
                .append(bucketName)
                .append(".")
                .append(endpoint)
                .append("/")
                .append(objectName);

        log.info("文件上传到:{}", stringBuilder.toString());

        return stringBuilder.toString();
    }
}

4. OSS工具类对象的创建

package com.sky.config;

import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 配置类,用于创建AliOssUtil对象
 */
@Configuration
@Slf4j
public class OssConfiguration {


    // 通过参数注入的方式,将Oss的配置信息传递进来
    // Bean 如果容器中没有AliOssUtil对象,则创建AliOssUtil对象
    // ConditionalOnBean 表示如果容器中存在AliOssUtil对象,则不创建AliOssUtil对象
    @Bean
    @ConditionalOnMissingBean
    public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
        log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);
        // 创建AliOssUtil对象,并传入配置信息
        return new AliOssUtil(aliOssProperties.getEndpoint(),
                              aliOssProperties.getAccessKeyId(),
                              aliOssProperties.getAccessKeySecret(),
                              aliOssProperties.getBucketName());
    }
}

5. 在Controller层编写文件上传逻辑

// Autowired注解的作用:自动将匹配的Bean装配到@Autowired标注的变量、方法或构造函数上。(依赖注入)
    @Autowired
    private AliOssUtil aliOssUtil;


    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> upload(MultipartFile file) {
        log.info("文件上传接口被调用:{}",file);
        String filePath = null;
        try {
            //  获取原始文件名
            String originalFilename = file.getOriginalFilename();
            // 截取原始文件名的扩展名
            String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 生成新的文件名: UUID + 扩展名
            String newFileName = UUID.randomUUID().toString() + extName;

            // 文件上传请求路径
            filePath = aliOssUtil.upload(file.getBytes(), newFileName);
        }catch(IOException e){
            e.printStackTrace();
            log.error("文件上传失败:{}",e);
            return Result.error(MessageConstant.UPLOAD_FAILED);
        }
       return Result.success(filePath);
    }

6. 测试(省略,做到这里基本无误了)

接下来就来讲讲文件回显失败的几点调试方向

文件回显失败的原因及其排查方向

1. 检查你文件上传拼接的url是否正确

这一步如果错了,实际上上传就已经错了,更别提回显这方面的事情了

2. 首先,如果你全程跟着苍穹老师写的代码去做,必然会导致文件回显失败,我记得视频里面的代码最后是没有返回文件上传路径的,你可以改一下再尝试

具体的,苍穹老师写的文件上传代码:

问题就出在成功的时候还封装了错误的信息进行返回,你应该要把错误的返回放到catch中,成功后把文件路径放到Result中

/**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> upload(MultipartFile file) {
        log.info("文件上传接口被调用:{}",file);
        try {
            //  获取原始文件名
            String originalFilename = file.getOriginalFilename();
            // 截取原始文件名的扩展名
            String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 生成新的文件名: UUID + 扩展名
            String newFileName = UUID.randomUUID().toString() + extName;

            // 文件上传请求路径
            String filePath  = aliOssUtil.upload(file.getBytes(), newFileName);
        }catch(IOException e){
            e.printStackTrace();
            log.error("文件上传失败:{}",e);
            
        }
       return Result.error(MessageConstant.UPLOAD_FAILED);
    }
3. 接着排查OSS,你得允许你的网页可以访问你的OSS对象存储服务之后才可以拿到文件的访问路径,使用OSS必须设置公共读属性

由于我只是练习程序,不想麻烦所以直接设置了公共读写。但是其实最安全的方式还是配置一个防盗链放行你的网页会比较安全

<think>我们正在讨论苍穹外卖系统的图片上传功能实现。根据引用[1][2][3],我们有一些关于前端页面和Java控制器的代码片段,但并没有直接展示图片上传的实现。不过,我们可以基于常见的Spring Boot和微信小程序开发模式来构建这个功能。 图片上传通常涉及两个部分: 1. 前端(微信小程序):使用小程序的API选择图片并上传到服务器。 2. 后端(Spring Boot):接收图片文件,保存到服务器或云存储,并返回访问路径。 下面我们分步骤实现: ### 前端实现(微信小程序) 在小程序端,我们可以使用`wx.chooseImage`选择图片,然后使用`wx.uploadFile`上传到服务器。 修改`index.wxml`,添加一个按钮和一个用于显示上传图片的image组件: ```html <view> <button bindtap="chooseImage">选择图片</button> <image src="{{tempFilePath}}" mode="widthFix" style="width:200px;height:200px"></image> <button bindtap="uploadImage">上传图片</button> </view> ``` 在`index.js`的Page的data中添加`tempFilePath`变量,用于存储临时图片路径,并添加两个事件处理函数: ```javascript Page({ data: { // ...原有数据 tempFilePath: '', // 临时文件路径 serverPath: '' // 服务器返回的图片路径 }, // 选择图片 chooseImage: function() { wx.chooseImage({ count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: (res) => { // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片 this.setData({ tempFilePath: res.tempFilePaths[0] }); } }); }, // 上传图片 uploadImage: function() { if (this.data.tempFilePath) { wx.uploadFile({ url: 'https://your-domain.com/user/setmeal/upload', // 替换为你的后端接口地址 filePath: this.data.tempFilePath, name: 'file', formData: { // 可以附加其他参数,例如菜品id等 }, success: (res) => { // 解析返回的数据,通常后端会返回图片的URL const data = JSON.parse(res.data); if (data.code === 1) { this.setData({ serverPath: data.data // 假设返回的data字段是图片URL }); wx.showToast({ title: '上传成功', }); } else { wx.showToast({ title: '上传失败', icon: 'error' }); } } }); } else { wx.showToast({ title: '请先选择图片', icon: 'none' }); } } }); ``` ### 后端实现Spring Boot) 在后端,我们需要一个接收文件上传的控制器。这里使用Spring Boot的MultipartFile接收文件。 首先,在`SetmealController`中添加一个处理图片上传的方法(或者可以考虑新建一个专门的文件上传控制器): ```java @PostMapping("/upload") @ApiOperation("图片上传") public Result<String> upload(MultipartFile file) { // 这里简单示例,实际项目中需要将文件保存到指定位置(本地或云存储)并返回访问URL if (file.isEmpty()) { return Result.error("上传文件不能为空"); } try { // 获取文件名 String fileName = file.getOriginalFilename(); // 生成新的文件名(避免重复) String newFileName = UUID.randomUUID().toString() + fileName.substring(fileName.lastIndexOf(".")); // 保存文件到服务器(例如保存到D盘的upload目录) String filePath = "D:/upload/"; File dest = new File(filePath + newFileName); if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } file.transferTo(dest); // 返回访问路径(这里返回的是本地路径,实际项目中应该返回通过HTTP能访问的URL) return Result.success(dest.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); return Result.error("上传失败"); } } ``` 注意:上述代码将文件保存在本地,实际生产环境一般会使用云存储(如阿里云OSS、腾讯云COS等)或者使用文件服务器,并返回一个公网可访问的URL。 ### 配置 1. 确保Spring Boot配置文件(application.yml)中设置文件上传大小限制(如果需要): ```yaml spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB ``` 2. 确保小程序端上传的URL正确,且后端服务允许跨域(如果前后端分离部署)。 ### 安全性考虑 - 文件类型检查:避免上传非图片文件,可以在后端检查文件扩展名或MIME类型。 - 文件大小限制:防止大文件攻击。 - 文件名随机化:避免文件名冲突和覆盖。 - 云存储:生产环境建议使用云存储服务,并设置适当的访问权限。 以上就是一个简单的图片上传功能的实现。 §§相关问题§§ 1. 如何在小程序端实现图片上传? 2. 如何将上传的图片保存到云存储(如阿里云OSS)? 3. 如何限制用户上传图片的类型和大小? 4. 如何在小程序端显示上传进度?</think>### 苍穹外卖图片上传功能实现指南 在苍穹外卖系统中实现图片上传功能,需要同时处理前端(微信小程序)和后端(Spring Boot)的开发。以下是完整实现方案: #### 一、前端实现(微信小程序) 1. **WXML页面布局** 添加图片选择器和上传按钮: ```html <view class="container"> <!-- 图片选择器 --> <button bindtap="chooseImage">选择图片</button> <image src="{{tempFilePath}}" mode="widthFix" style="width:200px;height:200px"></image> <!-- 上传按钮 --> <button type="primary" bindtap="uploadImage">上传图片</button> <text>上传结果:{{uploadResult}}</text> </view> ``` 2. **JS逻辑实现** ```javascript Page({ data: { tempFilePath: '', // 临时文件路径 uploadResult: '' // 上传结果 }, // 选择图片 chooseImage: function() { wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: (res) => { this.setData({ tempFilePath: res.tempFilePaths[0] }); } }); }, // 上传图片 uploadImage: function() { if (!this.data.tempFilePath) { wx.showToast({ title: '请先选择图片', icon: 'none' }); return; } wx.uploadFile({ url: 'https://your-domain.com/upload', // 替换为实际API地址 filePath: this.data.tempFilePath, name: 'file', formData: { 'type': 'food' }, // 附加参数 success: (res) => { const data = JSON.parse(res.data); this.setData({ uploadResult: data.data }); wx.showToast({ title: '上传成功' }); }, fail: (err) => { console.error('上传失败', err); } }); } }); ``` #### 二、后端实现Spring Boot) 1. **创建文件上传控制器** ```java @RestController @RequestMapping("/upload") @Api(tags = "文件上传接口") public class FileUploadController { @Value("${file.upload-dir}") private String uploadDir; // 配置文件存储路径 @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Result<String> uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("type") String type) { try { // 1. 生成唯一文件名 String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename(); // 2. 创建目标路径 Path targetPath = Paths.get(uploadDir).resolve(fileName); Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING); // 3. 返回访问URL(实际项目需配置CDN地址) String fileUrl = "/uploads/" + fileName; return Result.success(fileUrl); } catch (IOException e) { return Result.error("文件上传失败"); } } } ``` 2. **配置文件存储路径** 在`application.yml`中添加配置: ```yaml file: upload-dir: /var/www/upload/ # Linux路径 # upload-dir: D:/project/upload/ # Windows路径 ``` 3. **启用文件上传支持** 在启动类添加配置: ```java @Bean public MultipartConfigElement multipartConfigElement() { MultipartConfigFactory factory = new MultipartConfigFactory(); factory.setMaxFileSize("10MB"); factory.setMaxRequestSize("50MB"); return factory.createMultipartConfig(); } ``` #### 三、安全与优化建议 1. **安全措施**: - 文件类型校验:在服务端检查文件头信息 ```java if (!file.getContentType().startsWith("image/")) { throw new RuntimeException("仅支持图片格式"); } ``` - 文件重命名:避免原始文件名注入攻击 - 设置文件大小限制 2. **云存储方案**: - 推荐使用阿里云OSS或腾讯云COS替代本地存储 - 生成临时访问凭证(STS)保证安全 - 自动生成缩略图优化加载速度 3. **数据库集成**: ```java // 在菜品/套餐实体中添加图片字段 @Entity public class Dish { // ... @Column(name = "image_url") private String imageUrl; // 存储上传后的图片URL } ``` #### 四、部署注意事项 1. **Nginx静态资源配置**: ```nginx location /uploads/ { alias /var/www/upload/; expires 30d; # 缓存优化 } ``` 2. **跨域处理**(开发阶段): ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST"); } } ``` > 实际项目中建议使用云存储服务(如阿里云OSS),可直接生成前端直传签名,避免文件流经应用服务器,提高性能和安全性[^3]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值