parent
26c19221d8
commit
27f48ff43a
@ -0,0 +1,7 @@
|
|||||||
|
package com.luojia_channel.common.exception;
|
||||||
|
|
||||||
|
public class FileException extends BaseException{
|
||||||
|
public FileException(String msg){
|
||||||
|
super(500, msg);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "luojia-island",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.luojia_channel.modules.file.config;
|
||||||
|
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MinioConfig {
|
||||||
|
@Value("${minio.endpoint}")
|
||||||
|
private String endpoint;
|
||||||
|
@Value("${minio.accessKey}")
|
||||||
|
private String accessKey;
|
||||||
|
@Value("${minio.secretKey}")
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MinioClient minioClient() {
|
||||||
|
try {
|
||||||
|
MinioClient minioClient = MinioClient.builder()
|
||||||
|
.endpoint(endpoint)
|
||||||
|
.credentials(accessKey, secretKey)
|
||||||
|
.build();
|
||||||
|
return minioClient;
|
||||||
|
} catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.luojia_channel.modules.file.constants;
|
||||||
|
|
||||||
|
public class FileConstant {
|
||||||
|
public static final String CHUNK_BUCKET = "chunks";
|
||||||
|
public static final String CHUNK_PREFIX = "file:chunks:";
|
||||||
|
public static final long MAX_UPLOAD_SIZE = 10*1024*1024;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.luojia_channel.modules.file.dto;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
// 合并分片DTO
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class CompleteUploadDTO {
|
||||||
|
private String fileMd5;
|
||||||
|
|
||||||
|
private Integer totalChunks;
|
||||||
|
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
private String fileName;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.luojia_channel.modules.file.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.luojia_channel.modules.file.entity.LjFile;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface LjFileMapper extends BaseMapper<LjFile> {
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.luojia_channel.modules.file.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.luojia_channel.modules.file.dto.CompleteUploadDTO;
|
||||||
|
import com.luojia_channel.modules.file.dto.UploadChunkDTO;
|
||||||
|
import com.luojia_channel.modules.file.dto.UploadFileDTO;
|
||||||
|
import com.luojia_channel.modules.file.entity.LjFile;
|
||||||
|
|
||||||
|
public interface FileService extends IService<LjFile> {
|
||||||
|
Boolean createBucket(String name);
|
||||||
|
Boolean deleteBucket(String name);
|
||||||
|
Long uploadFile(UploadFileDTO uploadFileDTO);
|
||||||
|
Boolean uploadChunk(UploadChunkDTO chunkDTO);
|
||||||
|
Long completeUpload(CompleteUploadDTO completeDTO);
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
package com.luojia_channel.modules.file.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.luojia_channel.common.exception.FileException;
|
||||||
|
import com.luojia_channel.common.utils.RedisUtil;
|
||||||
|
import com.luojia_channel.modules.file.dto.UploadChunkDTO;
|
||||||
|
import com.luojia_channel.modules.file.dto.UploadFileDTO;
|
||||||
|
import com.luojia_channel.modules.file.entity.LjFile;
|
||||||
|
import com.luojia_channel.modules.file.mapper.LjFileMapper;
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import io.minio.StatObjectArgs;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.luojia_channel.modules.file.constants.FileConstant.*;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ValidateFileUtil {
|
||||||
|
private final LjFileMapper ljFileMapper;
|
||||||
|
private final RedisUtil redisUtil;
|
||||||
|
private final GeneratePathUtil generatePathUtil;
|
||||||
|
private final MinioClient minioClient;
|
||||||
|
// 验证分片参数
|
||||||
|
public void validateChunk(UploadChunkDTO chunkDTO) {
|
||||||
|
if (chunkDTO.getFile().isEmpty()) {
|
||||||
|
throw new FileException("分片数据不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(chunkDTO.getFileMd5())) {
|
||||||
|
throw new FileException("文件MD5缺失");
|
||||||
|
}
|
||||||
|
if (chunkDTO.getChunkNumber() < 0) {
|
||||||
|
throw new FileException("分片编号不能为负数");
|
||||||
|
}
|
||||||
|
if (chunkDTO.getTotalChunks() <= 0) {
|
||||||
|
throw new FileException("总分片数必须大于0");
|
||||||
|
}
|
||||||
|
if(chunkDTO.getChunkNumber() >= chunkDTO.getTotalChunks()){
|
||||||
|
throw new FileException("分片序号不能大于分片总数");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 校验普通文件上传
|
||||||
|
public void validateFile(UploadFileDTO dto) {
|
||||||
|
if (dto.getFile().isEmpty()) {
|
||||||
|
throw new FileException("文件不能为空");
|
||||||
|
}
|
||||||
|
// MD5校验
|
||||||
|
if (StrUtil.isBlank(dto.getFileMd5())) {
|
||||||
|
throw new FileException("文件MD5缺失");
|
||||||
|
}
|
||||||
|
// 类型校验
|
||||||
|
if (!Arrays.asList("image", "video").contains(dto.getFileType())) {
|
||||||
|
throw new FileException("不支持的文件类型");
|
||||||
|
}
|
||||||
|
// 大小校验
|
||||||
|
if(dto.getFile().getSize() > MAX_UPLOAD_SIZE){
|
||||||
|
throw new FileException("上传文件大小过大");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 秒传检查
|
||||||
|
public Long getExistedFileId(String fileMd5) {
|
||||||
|
LjFile file = ljFileMapper.selectOne(Wrappers.<LjFile>lambdaQuery()
|
||||||
|
.eq(LjFile::getFileMd5, fileMd5));
|
||||||
|
if(file == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return file.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查分片是否已上传
|
||||||
|
public boolean isChunkUploaded(String fileMd5, int chunkNumber) {
|
||||||
|
String objectName = generatePathUtil.getChunkObjectName(fileMd5, chunkNumber);
|
||||||
|
String redisKey = CHUNK_PREFIX + fileMd5;
|
||||||
|
// 检查redis中是否已记录该分片
|
||||||
|
if (redisUtil.sIsMember(redisKey, chunkNumber)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
minioClient.statObject(
|
||||||
|
StatObjectArgs.builder()
|
||||||
|
.bucket(CHUNK_BUCKET)
|
||||||
|
.object(objectName)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查所有分片是否已上传
|
||||||
|
public boolean areAllChunksUploaded(String fileMd5, int totalChunks) {
|
||||||
|
String key = CHUNK_PREFIX + fileMd5;
|
||||||
|
List<Integer> uploadedChunks = redisUtil.sGet(key);
|
||||||
|
return uploadedChunks.size() == totalChunks;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,38 @@
|
|||||||
package com.luojia_channel.modules.user.controller;
|
package com.luojia_channel.modules.user.controller;
|
||||||
|
|
||||||
|
import com.luojia_channel.common.domain.Result;
|
||||||
|
import com.luojia_channel.modules.file.dto.UploadFileDTO;
|
||||||
|
import com.luojia_channel.modules.user.dto.UserChangeInfoDTO;
|
||||||
|
import com.luojia_channel.modules.user.service.UserInfoService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/user/info")
|
@RequestMapping("/user/info")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserInfoController {
|
public class UserInfoController {
|
||||||
|
private final UserInfoService userInfoService;
|
||||||
|
@PostMapping("/update")
|
||||||
|
public Result<Void> updateInfo(@RequestBody UserChangeInfoDTO userChangeInfoDTO){
|
||||||
|
userInfoService.updateInfo(userChangeInfoDTO);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/password")
|
||||||
|
public Result<Void> updatePassword(@RequestParam String password){
|
||||||
|
userInfoService.updatePassword(password);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/avatar")
|
||||||
|
public Result<Void> updateAvatar(@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam("fileType") String fileType,
|
||||||
|
@RequestParam("fileMd5") String fileMd5) {
|
||||||
|
UploadFileDTO fileDTO = UploadFileDTO.builder()
|
||||||
|
.file(file).fileType(fileType).fileMd5(fileMd5)
|
||||||
|
.build();
|
||||||
|
userInfoService.updateAvatar(fileDTO);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.luojia_channel.modules.user.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserChangeInfoDTO {
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private String studentId;
|
||||||
|
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
private Integer gender;
|
||||||
|
|
||||||
|
private String college;
|
||||||
|
}
|
@ -1,7 +1,16 @@
|
|||||||
package com.luojia_channel.modules.user.service;
|
package com.luojia_channel.modules.user.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.luojia_channel.modules.file.dto.UploadFileDTO;
|
||||||
|
import com.luojia_channel.modules.user.dto.UserChangeInfoDTO;
|
||||||
import com.luojia_channel.modules.user.entity.User;
|
import com.luojia_channel.modules.user.entity.User;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
public interface UserInfoService extends IService<User> {
|
public interface UserInfoService extends IService<User> {
|
||||||
|
|
||||||
|
void updateInfo(UserChangeInfoDTO userChangeInfoDTO);
|
||||||
|
|
||||||
|
void updatePassword(String password);
|
||||||
|
|
||||||
|
void updateAvatar(UploadFileDTO uploadFileDTO);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.luojia_channel;
|
||||||
|
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class LuojiaChannelApplicationTests {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,8 +1,11 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router'; // 确保引入了 router
|
import router from './router'; // 确保引入了 router
|
||||||
|
import ELementPlus from 'element-plus';
|
||||||
|
import 'element-plus/dist/index.css';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
app.use(router); // 注册 vue-router
|
app.use(router); // 注册 vue-router
|
||||||
|
app.use(ELementPlus); // 注册 element-plus
|
||||||
app.mount('#app');
|
app.mount('#app');
|
@ -0,0 +1,36 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const request = axios.create({
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
|
||||||
|
//响应拦截器
|
||||||
|
request.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(response.data);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.error('请求错误:', error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//请求拦截器
|
||||||
|
request.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
console.log('Request:',config);
|
||||||
|
const token = localStorage.getItem('accessToken');
|
||||||
|
if (token) {
|
||||||
|
config.headers['Authorization'] = token;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default request;
|
@ -1,4 +1,16 @@
|
|||||||
const { defineConfig } = require('@vue/cli-service')
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
transpileDependencies: true
|
transpileDependencies: true,
|
||||||
|
devServer: {
|
||||||
|
port: 8080,
|
||||||
|
proxy: {
|
||||||
|
'/user': {
|
||||||
|
target: 'http://localhost:8081',
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: {
|
||||||
|
'^/user': '/user'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in new issue