图片上传(前端+后端)

一、界面(其中有三个图标add.png,yulan.png,shanchu.png可以在iconfont-阿里巴巴矢量图标库这里这搜索下载-分别是添加,浏览,删除)

<el-upload
        action
        list-type="picture-card"
        :auto-upload="false"
        :file-list="pics"
        :limit="1"
        :on-change="handleChange"
        :on-success="handleSuccess"
        :on-error="handleError"
        :on-exceed="handleExceed"
        ref="pictureEditRef"
        :http-request="submitFile"
    >
      <img src="../../../assets/add.png" style="width: 40px">
      <template slot="file" #file="file">
        <img class="el-upload-list__item-thumbnail" :src="file.file.url" alt/>
        <span class="el-upload-list__item-actions">
                <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
                  <img src="../../../assets/yulan.png" style="width: 20px">
                </span>
                <span
                    v-if="!disabled"
                    class="el-upload-list__item-delete"
                    @click="handleRemove(file)"
                >
                  <img src="../../../assets/shanchu.png" style="width: 20px">
                </span>
              </span>
      </template>
    </el-upload>

二、放大弹窗

 <el-dialog v-model="dialogVisible" :append-to-body="true">
      <img width="100%" :src="dialogImageUrl" alt/>
    </el-dialog>

三、初始化

 pics: [],
 disabled: false,
 isPicChanged: false,
 dialogImageUrl: "",
 dialogVisible: false,

四、js(其中this.ruleForm.newsPhoto换成你的图片属性)

handleRemove(file) {
      this.pics = []; // 移除图片时清空图片列表
      this.ruleForm.newsPhoto = ''; // 同时清空ruleForm中的图片地址
    },
    handlePictureCardPreview(file) {
      this.dialogImageUrl = file.file.url;
      this.dialogVisible = true;
    },
    handleChange(file, filelist) {
      this.pics = filelist.slice(0); // 更新图片列表
      this.isPicChanged = true;
    },
    handleSuccess(response) {
      if (response.code === 0) {
        this.ruleForm.newsPhoto = response.data.file; // 上传成功后保存图片地址
      } else {
        this.$message.error(response.msg);
      }
    },
    handleError(err) {
      console.log("上传失败");
      console.log(err);
    },
    handleExceed() {
      this.$message.error("图片最多上传一张!");
    },
    async submitFile() {
      return new Promise((resolve, reject) => {
        if (this.pics.length > 0 && this.pics[0].raw) {
          let formData = new FormData();
          formData.append("file", this.pics[0].raw);
          this.$http({
            url: 'file/upload',
            method: "post",
            data: formData
          }).then(({data}) => {
            if (data.code === 0) {
              this.ruleForm.newsPhoto = 'upload/' + data.file;
              resolve();
            } else {
              this.$message.error(data.msg);
              reject();
            }
          }).catch(() => {
            this.$message.error('图片上传失败');
            reject();
          });
        } else {
          resolve();
        }
      });
    },

五、图片初始化赋值(这个是修改或者详情回显用到的,在进页面获取当前图片数据是赋值给pics,其中this.$base.url是定义的全局属性,是你的后端端口路径)

this.pics.push({
   url: this.$base.url+_this.ruleForm.newsPhoto,
});

六、修改时如果图片存在且没有修改可以在提交时添加一下判断(如果不需要修改就忽略,不添加会导致图片每次提交都会重新上传但不影响正常使用)

if (this.isPicChanged) {
   this.submitFile().then(() => {
        //这里放你的提交代码
   })
}

七、后端接口方法

package com.controller;

import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.annotation.IgnoreAuth;
import com.entity.EIException;
import com.utils.R;

/**
 * 上传文件映射表
 */
@RestController
@RequestMapping("file")
public class FileController{
	/**
	 * 上传文件
	 */
	@IgnoreAuth
	@RequestMapping("/upload")
	public R upload(@RequestParam("file") MultipartFile file) throws Exception {
		if (file.isEmpty()) {
			throw new EIException("上传文件不能为空");
		}
		String fileExt = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")+1);
		Path projectPath = Paths.get("").toAbsolutePath();
		File path = new File(projectPath+"/src/main/resources/static");
		if(!path.exists()) {
			path = new File("");
		}
		File upload = new File(path,"/upload/");
		if(!upload.exists()) {
			upload.mkdirs();
		}

		File path2 = new File(projectPath+"/target/classes/static");
		if(!path2.exists()) {
			path2 = new File("");
		}
		File upload2 = new File(path2,"/upload/");
		if(!upload2.exists()) {
			upload2.mkdirs();
		}
		String fileName = UUID.randomUUID().toString() + "." + fileExt;
		File dest = new File(upload+"/"+fileName);
		file.transferTo(dest);
		// 创建文件的副本
		File destFile = new File(upload2+"/"+fileName);
		try (FileInputStream fis = new FileInputStream(dest);
			 FileOutputStream fos = new FileOutputStream(destFile)) {
			byte[] buffer = new byte[1024];
			int length;
			// 从源文件读取内容并写入到目标文件
			while ((length = fis.read(buffer)) > 0) {
				fos.write(buffer, 0, length);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return R.ok().put("file", fileName);
	}

	/**
	 * 下载文件
	 */
	@IgnoreAuth
	@RequestMapping("/download")
	public ResponseEntity<byte[]> download(@RequestParam String fileName) {
		try {
			File path = new File(ResourceUtils.getURL("classpath:static").getPath());
			if(!path.exists()) {
			    path = new File("");
			}
			File upload = new File(path.getAbsolutePath(),"/upload/");
			if(!upload.exists()) {
			    upload.mkdirs();
			}
			File file = new File(upload.getAbsolutePath()+"/"+fileName);
			if(file.exists()){
				/*if(!fileService.canRead(file, SessionManager.getSessionUser())){
					getResponse().sendError(403);
				}*/
				HttpHeaders headers = new HttpHeaders();
			    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
			    headers.setContentDispositionFormData("attachment", fileName);
			    return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers, HttpStatus.CREATED);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return new ResponseEntity<byte[]>(HttpStatus.INTERNAL_SERVER_ERROR);
	}

}

<think>好的,用户想了解如何在后端实现图片上传功能,需要方法和相关的技术栈。首先,我得回忆一下常见的后端技术栈,比如Node.js、Python、Java这些。然后考虑图片上传的基本流程,用户上传文件,后端接收处理,存储到服务器或者云存储,可能还需要生成访问链接。 接下来,要分步骤说明。第一步是接收文件,可能需要使用中间件,比如Express的multer,或者Spring Boot的MultipartFile。然后处理文件,比如校验格式、大小,重命名防止冲突。存储部分有两种选择,本地存储或云存储,云存储比如AWS S3、阿里云OSS这些,可能更常用,因为扩展性好。之后生成访问链接,如果是云存储直接返回URL,本地的话可能需要配置静态资源服务。 技术栈方面,不同语言有不同的框架,比如Node.js用Express、Koa,Python用Django、Flask,Java用Spring Boot。存储服务可以选云服务商或者自建。数据库可能需要记录文件信息,比如MySQL、MongoDB。 安全方面要注意限制文件类型和大小,防止恶意上传。还有病毒扫描,可能用ClamAV之类的工具。另外,身份验证和权限控制,确保只有授权用户能上传。 然后生成相关问题,可能涉及具体框架的实现,比如Spring Boot怎么处理,或者如何做断点续传,还有安全措施,以及云存储和本地存储的比较。需要确保问题覆盖用户可能关心的其他方面。 最后检查是否符合用户的要求,特别是格式方面,行内公式用$,独立公式用$$,但这里可能用不到数学公式,所以主要注意代码块和引用。引用标记要正确,参考的引用[^1]是关于后端技术栈的,虽然原文内容不详细,但可以关联到技术栈部分。确保回答结构清晰,步骤明确,技术栈列举全面,安全考虑到位。</think>### 后端实现图片上传功能的方法及技术栈 #### 一、实现步骤 1. **接收文件上传请求** 客户端通过HTTP协议的`POST`方法上传文件,使用`multipart/form-data`格式传输数据。后端需通过中间件解析请求体,例如: ```javascript // Node.js Express示例 const multer = require('multer'); const upload = multer({ dest: 'uploads/' }); app.post('/upload', upload.single('image'), (req, res) => { // 处理文件 }); ``` 2. **文件处理与校验** - **格式校验**:限制文件类型(如仅允许`jpg/png`),通过文件头或扩展名验证。 - **大小限制**:通过配置中间件限制文件大小(如`10MB`)。 - **重命名**:生成唯一文件名(如`UUID`)防止覆盖。 3. **存储方案选择** - **本地存储**:直接保存到服务器磁盘,需配置静态资源访问路径。 ```python # Flask示例 from flask import send_from_directory @app.route('/uploads/<filename>') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) ``` - **云存储**(推荐):使用对象存储服务(如AWS S3、阿里云OSS),通过SDK上传并获取访问URL。 4. **返回文件访问信息** 生成文件唯一标识或直接返回云存储的公开URL,例如: ```java // Spring Boot示例 public String uploadFile(MultipartFile file) { String url = cloudStorage.upload(file); return "{\"url\":\"" + url + "\"}"; } ``` --- #### 二、技术栈推荐 1. **开发框架** - **Node.js**:Express/Koa + Multer中间件 - **Python**:Django/Flask + `python-multipart`库 - **Java**:Spring Boot + `MultipartFile` 2. **存储服务** - **云服务商**:AWS S3、阿里云OSS、腾讯云COS - **自建方案**:MinIO(兼容S3协议的开源对象存储) 3. **数据库** 存储文件元信息(文件名、路径、上传者等),常用MySQL/MongoDB。 --- #### 三、安全注意事项 1. **文件类型白名单**:禁止可执行文件(如`.php`)上传,防止恶意脚本攻击。 2. **病毒扫描**:集成ClamAV等工具扫描上传内容。 3. **访问权限控制**:通过JWT/OAuth2验证用户身份,限制上传频率。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值