[add] file upload
This commit is contained in:
parent
0f09f4bb65
commit
3f3fe5221c
14
src/main/java/com/lk/paopao/conf/AppUploadProperties.java
Normal file
14
src/main/java/com/lk/paopao/conf/AppUploadProperties.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.lk.paopao.conf;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "app.upload")
|
||||
public class AppUploadProperties {
|
||||
String baseUrl;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.lk.paopao.controller;
|
||||
|
||||
|
||||
import com.lk.paopao.dto.AttachmentDto;
|
||||
import com.lk.paopao.dto.rest.response.DataResult;
|
||||
import com.lk.paopao.dto.rest.response.ResultUtil;
|
||||
import com.lk.paopao.service.AttachService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
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 java.io.IOException;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("${app.version}/attachment")
|
||||
public class AttachmentController {
|
||||
@Autowired
|
||||
AttachService attachService;
|
||||
|
||||
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public DataResult<AttachmentDto> upload(@RequestParam("file") MultipartFile file) throws IOException {
|
||||
|
||||
AttachmentDto dto = attachService.upload(file);
|
||||
return ResultUtil.ok(dto);
|
||||
}
|
||||
}
|
20
src/main/java/com/lk/paopao/dto/AttachmentDto.java
Normal file
20
src/main/java/com/lk/paopao/dto/AttachmentDto.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.lk.paopao.dto;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* DTO for {@link com.lk.paopao.entity.Attachment}
|
||||
*/
|
||||
@Value
|
||||
public class AttachmentDto implements Serializable {
|
||||
Long id;
|
||||
Long fileSize;
|
||||
Integer imgWidth;
|
||||
Integer imgHeight;
|
||||
Byte type;
|
||||
String content;
|
||||
Long createdOn;
|
||||
Long userId;
|
||||
}
|
65
src/main/java/com/lk/paopao/entity/Attachment.java
Normal file
65
src/main/java/com/lk/paopao/entity/Attachment.java
Normal file
@ -0,0 +1,65 @@
|
||||
package com.lk.paopao.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Comment("附件")
|
||||
@Entity
|
||||
@Table(name = "p_attachment", indexes = {
|
||||
@Index(name = "idx_attachment_user", columnList = "user_id")
|
||||
})
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class Attachment {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "file_size", nullable = false)
|
||||
private Long fileSize = 0L;
|
||||
|
||||
@ColumnDefault("0")
|
||||
@Column(name = "img_width", nullable = false)
|
||||
private Integer imgWidth = 0;
|
||||
|
||||
@ColumnDefault("0")
|
||||
@Column(name = "img_height", nullable = false)
|
||||
private Integer imgHeight = 0;
|
||||
|
||||
@Comment("1图片,2视频,3其他附件")
|
||||
@Column(name = "type", nullable = false)
|
||||
private Byte type = 1;
|
||||
|
||||
@Column(name = "content", nullable = false)
|
||||
private String content;
|
||||
|
||||
@Comment("创建时间")
|
||||
@CreatedDate
|
||||
@Column(name = "created_on", nullable = false)
|
||||
private Long createdOn;
|
||||
|
||||
@Comment("修改时间")
|
||||
@LastModifiedDate
|
||||
@Column(name = "modified_on", nullable = false)
|
||||
private Long modifiedOn;
|
||||
|
||||
@Comment("删除时间")
|
||||
@Column()
|
||||
private Long deletedOn = 0L;
|
||||
|
||||
@Comment("是否删除 0 为未删除、1 为已删除")
|
||||
@Column(name = "is_del", nullable = false)
|
||||
private byte isDel = 0;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
}
|
30
src/main/java/com/lk/paopao/mapper/AttachmentMapper.java
Normal file
30
src/main/java/com/lk/paopao/mapper/AttachmentMapper.java
Normal file
@ -0,0 +1,30 @@
|
||||
package com.lk.paopao.mapper;
|
||||
|
||||
import com.lk.paopao.conf.AppUploadProperties;
|
||||
import com.lk.paopao.dto.AttachmentDto;
|
||||
import com.lk.paopao.entity.Attachment;
|
||||
import org.mapstruct.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = MappingConstants.ComponentModel.SPRING)
|
||||
public abstract class AttachmentMapper {
|
||||
|
||||
@Autowired
|
||||
AppUploadProperties appUploadProperties ;
|
||||
|
||||
@Mapping(source = "user.id", target = "userId")
|
||||
@Mapping(target = "content", expression = "java(addHttpPrefix(attachment.getContent()))")
|
||||
public abstract AttachmentDto toDto(Attachment attachment);
|
||||
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
public abstract Attachment partialUpdate(AttachmentDto attachmentDto, @MappingTarget Attachment attachment);
|
||||
|
||||
String addHttpPrefix(String content) {
|
||||
if (content != null && !content.startsWith("http://") && !content.startsWith("https://")) {
|
||||
// 添加HTTP前缀并去掉"//"
|
||||
return appUploadProperties.getBaseUrl() + content;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.lk.paopao.repository;
|
||||
|
||||
import com.lk.paopao.entity.Attachment;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface AttachmentRepository extends JpaRepository<Attachment, Long> {
|
||||
}
|
151
src/main/java/com/lk/paopao/service/AttachService.java
Normal file
151
src/main/java/com/lk/paopao/service/AttachService.java
Normal file
@ -0,0 +1,151 @@
|
||||
package com.lk.paopao.service;
|
||||
|
||||
import com.lk.paopao.dto.AttachmentDto;
|
||||
import com.lk.paopao.entity.Attachment;
|
||||
import com.lk.paopao.entity.User;
|
||||
import com.lk.paopao.mapper.AttachmentMapper;
|
||||
import com.lk.paopao.repository.AttachmentRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class AttachService {
|
||||
|
||||
// 从配置中获取上传路径
|
||||
@Value("${app.upload.path}")
|
||||
String UPLOAD_DIR;
|
||||
|
||||
// 从配置中获取子路径名长度,用于文件分目录存储
|
||||
@Value("${app.upload.sub-path-name-length}")
|
||||
int SUB_PATH_LENGTH = 1;
|
||||
|
||||
@Autowired
|
||||
AuthService authService;
|
||||
@Autowired
|
||||
AttachmentRepository attachmentRepository;
|
||||
@Autowired
|
||||
AttachmentMapper attachmentMapper;
|
||||
|
||||
|
||||
/**
|
||||
* 上传附件
|
||||
* @param file 文件
|
||||
* @param attachType 附件类型
|
||||
* @return AttachmentDto
|
||||
* @throws IOException 输入输出异常
|
||||
*/
|
||||
public AttachmentDto upload(MultipartFile file) throws IOException {
|
||||
// 获取原始文件名
|
||||
String originFileName = file.getOriginalFilename();
|
||||
if(originFileName == null){
|
||||
throw new IllegalArgumentException("没有原始文件名");
|
||||
}
|
||||
|
||||
InputStream inputStream = file.getInputStream();
|
||||
// 获取当前用户
|
||||
User currentUser = authService.getCurrentUser();
|
||||
// 上传文件并获取相对URL
|
||||
String relativeUrl = save(originFileName,inputStream);
|
||||
Attachment attachment = new Attachment();
|
||||
// 设置附件类型
|
||||
attachment.setType(getType(file.getContentType()));
|
||||
// 设置附件内容(即存储的URL)
|
||||
attachment.setContent(relativeUrl);
|
||||
// 关联用户
|
||||
attachment.setUser(currentUser);
|
||||
// 设置文件大小
|
||||
attachment.setFileSize(file.getSize());
|
||||
// 保存附件
|
||||
Attachment savedAttachment = attachmentRepository.save(attachment);
|
||||
// 转换并返回附件DTO
|
||||
return attachmentMapper.toDto(savedAttachment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存文件到上传目录中
|
||||
* @param fileName 原文件名
|
||||
* @param fileStream 文件流
|
||||
* @return 保存后的文件访问路径
|
||||
* @throws IOException 文件操作异常
|
||||
*/
|
||||
public String save(String fileName, InputStream fileStream) throws IOException {
|
||||
// 生成新的文件名以避免重复
|
||||
String rename = UUID.randomUUID().toString();
|
||||
int dotIndex = fileName.lastIndexOf('.');
|
||||
// 新文件名,包含扩展名
|
||||
String newFile = rename+((dotIndex > 0) ? "."+fileName.substring(dotIndex + 1) : "");
|
||||
|
||||
// 根据子路径长度计算文件保存的子目录
|
||||
String subPath = rename.substring(0,SUB_PATH_LENGTH);
|
||||
// 完整的保存路径
|
||||
String uploadPath = UPLOAD_DIR+"/"+subPath;
|
||||
|
||||
// 确保目录存在,不存在则创建
|
||||
Path directory = Paths.get(uploadPath);
|
||||
if (!Files.exists(directory)) {
|
||||
Files.createDirectories(directory);
|
||||
}
|
||||
// 文件的最终保存位置
|
||||
Path targetLocation = Paths.get(uploadPath, newFile);
|
||||
// 复制文件流到目标位置
|
||||
Files.copy(fileStream, targetLocation);
|
||||
// 返回文件的访问路径
|
||||
return "/"+subPath+"/"+newFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件的MIME类型返回文件类型标识
|
||||
* @param contentType 文件的MIME类型
|
||||
* @return 文件类型的标识字节,0表示未知类型
|
||||
*/
|
||||
public static byte getType(String contentType) {
|
||||
// 存储MIME类型与文件类型标识的映射
|
||||
Map<String,Byte> map = new HashMap<>();
|
||||
|
||||
// 初始化映射,定义各类文件的类型标识
|
||||
map.put("image/jpeg", (byte)1);
|
||||
map.put("image/png", (byte)1);
|
||||
map.put("image/gif", (byte)1);
|
||||
map.put("image/bmp", (byte)1);
|
||||
map.put("image/webp", (byte)1);
|
||||
map.put("image/svg+xml", (byte)1);
|
||||
map.put("image/tiff", (byte)1);
|
||||
map.put("image/x-icon", (byte)1);
|
||||
map.put("image/vnd.microsoft.icon", (byte)1);
|
||||
map.put("video/mp4", (byte)2);
|
||||
map.put("video/x-msvideo", (byte)2);
|
||||
map.put("video/x-ms-wmv", (byte)2);
|
||||
map.put("video/x-flv", (byte)2);
|
||||
map.put("video/quicktime", (byte)2);
|
||||
map.put("video/x-matroska", (byte)2);
|
||||
map.put("video/webm", (byte)2);
|
||||
map.put("video/ogg", (byte)2);
|
||||
map.put("application/zip", (byte)3);
|
||||
map.put("application/x-7z-compressed", (byte)3);
|
||||
map.put("application/x-rar-compressed", (byte)3);
|
||||
map.put("application/x-tar", (byte)3);
|
||||
map.put("application/x-gzip", (byte)3);
|
||||
map.put("application/x-bzip2", (byte)3);
|
||||
map.put("application/x-xz", (byte)3);
|
||||
map.put("application/pdf", (byte)4);
|
||||
map.put("application/msword", (byte)4);
|
||||
// 获取文件类型标识
|
||||
Byte type = map.get(contentType);
|
||||
//获取不到type,则返回0
|
||||
return type==null?0:type;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,11 +25,14 @@ spring:
|
||||
defer-datasource-initialization: true
|
||||
database-platform: org.hibernate.dialect.H2Dialect
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
ddl-auto: update
|
||||
# 可选值:create-drop,create,update,none. create-drop:每次启动项目都会删除表,然后重新创建表,适合开发环境;create:每次启动项目都会创建表,适合开发环境;update:每次启动项目都会更新表,适合开发环境;none:不执行任何操作,适合生产环境。
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
# 静态资源目录配置
|
||||
resources:
|
||||
static-locations: classpath:/static/,file:/home/whz/tmp/ # 指定静态资源的位置
|
||||
|
||||
app:
|
||||
version: v1
|
||||
@ -39,7 +42,7 @@ app:
|
||||
jwt:
|
||||
secret-key: uYuVw0mke38MfLhO19wUQyRgwrmYo89ibpQTXPHi4vg=
|
||||
expiration: 36000000
|
||||
white-list:
|
||||
white-list: # 白名单
|
||||
- path: "/api-docs/**"
|
||||
methods: ["GET","PUT","POST"]
|
||||
- path: "/swagger-ui/**"
|
||||
@ -48,5 +51,11 @@ app:
|
||||
methods: [ "GET" ]
|
||||
- path: "/v1/auth/**"
|
||||
methods: [ "GET","PUT","POST"]
|
||||
allowed-origins:
|
||||
- "http://localhost:5173"
|
||||
- path: "/upload/**"
|
||||
methods: [ "GET" ]
|
||||
allowed-origins: # 允许跨域的域名
|
||||
- "http://localhost:5173"
|
||||
upload:
|
||||
path: /home/whz/tmp/upload # 上传文件路径
|
||||
sub-path-name-length: 2 # 上传文件路径子目录长度
|
||||
base-url: http://localhost:8080/upload # 上传文件路径前缀
|
Loading…
Reference in New Issue
Block a user