Multipart File 에서 transferto 방법 에 대한 구덩이

프롤로그:최근 에 SpringBoot 로 파일 업로드 기능 을 쓰 고 있 습 니 다.매개 변 수 를 바 인 딩 한 후에 정말 편리 합 니 다.
하지만 프로젝트 배치 에 문제 가 생 겨 어리둥절 했다.
나중에 야 알 게 된 것 은 제 가 상대 적 인 경 로 를 사 용 했 기 때 문 입 니 다.이것 은 사람 을 함정 에 빠 뜨리 는 곳 이지 만 배 워 야 할 것 이 많다 는 것 을 설명 합 니 다!
사례 재현

@PostMapping("/uploadFile")
public String uploadImg(@RequestParam("file") MultipartFile file, @RequestParam("equipmentId") String equipmentId) {
String baseDir = "./imgFile";  //             
  if (!file.isEmpty()) {
      String name = file.getOriginalFilename();
      String prefix = name.lastIndexOf(".") != -1 ? name.substring(name.lastIndexOf(".")) : ".jpg";
      String path = UUID.randomUUID().toString().replace("-", "") + prefix;
      try {
      	//            
          File filePath = new File(baseDir, path);
          //         ,       
          logger.info("      :{},    :{}", filePath.getParentFile().exists(), filePath.getParent());
          if (!filePath.getParentFile().exists()) {   //              ,    。
              filePath.getParentFile().mkdirs();
          }
          //        ,          ,           
          logger.info("      :{},    :{}", filePath.getParentFile().exists(), filePath.getParent());
          //         ,      !
          //     :filePath
          //     :filePath.getAbsoluteFile()
          logger.info("         :{}", filePath.getPath());
          file.transferTo(filePath);
          logger.info("         :{}", filePath.getAbsolutePath());
          return "    ";
      } catch (Exception e) {
          logger.error(e.getMessage());
      }
  }
  return "    ";
}
로그 에 경 로 를 인쇄 한 위 치 는 문제 가 없 음 을 보 여 줍 니 다.그 때 file.transferTo(filePath)가 실행 되면;FileNotFoundException 이 생 성 되 지만 제 앞의 코드 는 실행 되 었 고 폴 더 를 만 들 었 습 니 다.
Postman 테스트 캡 처
在这里插入图片描述
로그 출력
2020-11-27 10:15:06.519 INFO 5200---[nio-8080-exec-1]r.controller.LearnController:파일 저장 경로:false,존재 여부:.\imgFile
2020-11-27 10:15:06.521 INFO 5200---[nio-8080-exec-1]r.controller.LearnController:파일 저장 경로:true,존재 여부:.\imgFile
2020-11-27 10:15:06.521 INFO 5200---[nio-8080-exec-1]r.controller.LearnController:파일 을 저장 할 경로:.\imgFile\\684918a 520684801 b658c85a02bf9ba 5.jpg
2020-11-27 10:15:06.522 ERROR 5200 --- [nio-8080-exec-1] r.controller.LearnController : java.io.FileNotFoundException: C:\Users\Alfred\AppData\Local\Temp
\tomcat.080.23888705947355119\\work\Tomcat\\localhost\\ROOT\\\.\imgFile\\684918a 520684801b 658 c85a 02bf9ba 5.jpg(시스템 에서 지정 한 경 로 를 찾 을 수 없습니다.)
메모:여 기 는 갈 피 를 잡 을 수 없 지만 로 그 를 살 펴 보면 프로그램 이 이상 한 디 렉 터 리 에 파일 을 저장 하려 고 했 습 니 다.이 디 렉 터 리 가 앞의 filePath 와 관계 가 없다 고 생각 합 니 다.여 기 는 의문점 입 니 다!
실행 후 코드 가 있 는 디 렉 터 리 아래 에 imgFile 디 렉 터 리 가 만 들 어 졌 습 니 다.
在这里插入图片描述
imgFile 폴 더 가 비어 있 습 니 다.transferto 를 실행 할 때 이상 을 던 졌 기 때 문 입 니 다.
在这里插入图片描述
이 곳 에서 전 달 된 매개 변 수 를 파일 의 절대 경로 로 변경 합 니 다.

file.transferTo(filePath.getAbsoluteFile());
Postman 테스트 캡 처
업로드 성공!
在这里插入图片描述
실행 후 코드 가 있 는 디 렉 터 리 아래 에 imgFile 디 렉 터 리 가 만 들 어 졌 습 니 다.
在这里插入图片描述
imgFile 폴 더 에 올 린 그림 이 있 습 니 다.
在这里插入图片描述
원인 분석
위의 실패 와 성공 은 단지 경로 가 상대 적 인 경로 와 절대적 인 경로 의 차 이 를 대표 하기 때문이다.이 는 MultiparFile 의 transferto 방법 에 문제 가 있다 는 뜻 이다.정지점 을 하나 더 해서 디 버 깅 을 해 봅 시다!debug!
debug 의 작은 지식 추가:
debug tips:
step into:한 단계 실행,하위 함수 만 만나면 들 어가 서 한 단계 계속 실행(F5)
step over:한 단계 로 실행 할 때 함수 에서 하위 함 수 를 만 났 을 때 하위 함수 에 들 어가 서 한 단계 로 실행 하지 않 고 하위 함 수 를 전체적으로 실행 한 다음 에 멈 추 는 것 입 니 다.즉,하위 함수 전 체 를 한 단계 로 하 는 것 입 니 다(F6)
step return:하위 함수 에 한 걸음 에 실 행 될 때 step return 으로 하위 함수 의 나머지 부분 을 실행 하고 이전 층 으로 돌아 갈 수 있 습 니 다.
setp out:효 과 는 step return 과 같 습 니 다.
file.transferto(filePath.getAbsoluteFile()만 드 리 겠 습 니 다.이 줄 코드 는 정지점 을 추 가 했 습 니 다.여기 서 디 버 깅 에서 가장 중요 한 두 가지 절 차 를 보 여 드 리 겠 습 니 다.
디 버 깅 중 코드 의 실행 절 차 는:
그러나 코드 가 transferto 에 들 어간 후에 this.part.write(dest.getpath)방법 을 실행 하고 write 방법 내부 에 들 어가 면 여기까지 우리 의 답 을 얻 을 수 있 습 니 다!

@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
	this.part.write(dest.getPath());
	if (dest.isAbsolute() && !dest.exists()) {
		// Servlet 3.0 Part.write is not guaranteed to support absolute file paths:
		// may translate the given path to a relative location within a temp dir
		// (e.g. on Jetty whereas Tomcat and Undertow detect absolute paths).
		// At least we offloaded the file from memory storage; it'll get deleted
		// from the temp dir eventually in any case. And for our user's purposes,
		// we can manually copy it to the requested location as a fallback.
		FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest.toPath()));
	}
}
@Override
public void write(String fileName) throws IOException {
	File file = new File(fileName);
	if (!file.isAbsolute()) {
		file = new File(location, fileName);
	}
	try {
		fileItem.write(file);
	} catch (Exception e) {
		throw new IOException(e);
	}
}
이 write 방법 은 들 어 오 는 매개 변수 가 상대 적 인 경로 인지 판단 합 니 다.상대 적 인 경로 라면 스스로 부모 경 로 를 연결 합 니 다!그 러 니까 그 이상 한 경로 가 어디서 났 는 지 알 겠 지!
C:\Users\Alfred\AppData\Local\Temp\tomcat.8080.2388870592947355119\work\Tomcat\localhost\ROOT\.\imgFile\684918a520684801b658c85a02bf9ba5.jpg
자,대충 정리 할 수 있 습 니 다.이것 은 transferTo 의 매개 변수 때 문 입 니 다.상대 경로 라면 프로그램 은 부모 경 로 를 스스로 연결 합 니 다.제 가 지정 한 상대 경로 에 존재 하지 않 는 경로 가 있 기 때문에 저장 을 시도 하면 실패 할 것 입 니 다.하지만 입력 한 인자 가 파일 이름 이 라면 저장 에 성공 할 수 있 을 것 입 니 다.그러나 이렇게 해서 파일 을 찾 을 때 또 문제 가 생 길 수 있 습 니 다.파일 이 어디 에 있 는 지 모 를 수도 있 습 니 다!
보충 해 주세요.
여기 에는 또 하나의 재 미 있 는 곳 이 있다.만약 나의 상대 적 인 경로 에서 사용 하지 않 는 다 면,시작 은/로 만 시작 하면,또 하나의 재 미 있 는 상황 이 발생 할 것 이다.첫 번 째 상황 은 아까 그 랬 더 라 도 두 번 째 상황 을 토론 하 겠 습 니 다.이런 상황 은 윈도 시스템 에서 첫 번 째 와 같은 오류 이지 만 리 눅 스 시스템 에 서 는 정상적으로 실 행 될 수 있 습 니 다.두 시스템 의 지식 을 조금 알 고 있다 면 리 눅 스 시스템 의 루트 경 로 는/이 므 로/로 시작 하 는 경로 가 절대적 인 경로 라 는 것 을 알 아야 한다.
그래서 이것 도 프로그램 크로스 플랫폼 에서 고려 해 야 할 문제 입 니 다.리 눅 스 를 모 르 면 모 를 수도 있 습 니 다.여기 서 제 가 검증 프로그램 을 실제 테스트 해 보 겠 습 니 다.
윈도 시스템 과 리 눅 스 시스템 의 운행 결과 가 다른 코드.

import java.io.File;
import java.io.IOException;
public class OSMain {	
	public static void main(String[] args) {
		String path1 = "./hehe";
		String path2 = "/haha";
		File file1 = new File(path1);
		File file2 = new File(path2);		
		System.out.println("file1: " + file1 + " file1      ? " + file1.isAbsolute());
		System.out.println("file2: " + file1 + " file2      ? " + file2.isAbsolute());
		try {
			System.out.println(file1.getCanonicalPath());
			System.out.println(file2.getCanonicalPath());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
}
Windows 실행 결과
在这里插入图片描述
Linux 실행 결과
여기 에는 리 눅 스 환경 이 필요 하지만 내 컴퓨터 에는 없다.비록 나 는 아 리 클 라 우 드 서버 를 한 대 샀 지만.그러나 이렇게 작은 코드 를 위해 아 리 클 라 우 드 서버 에 로그 인하 여 실행 하기 위해 서 나 는 또 귀 찮 게 생각한다.다행히 나 는 더욱 교묘 한 방법 이 생각 났 다!
예전 에 위 에 있 는 질문 이 초보 튜 토리 얼 에 관 한 것 이 었 다 는 것 을 알 고 있 었 습 니 다.그리고 초보 튜 토리 얼 의 작가 가 직접 나 와 서 문 제 를 대답 하고 그림-초보 튜 토리 얼 기술 구조 도 보 를 붙 였 습 니 다.

이 사진 자 체 는 사실 많은 것 과 관련 이 있 습 니 다.그러나 우 리 는 온라인 코드 제출 집행 에 만 관심 을 가지 고 있 습 니 다.그 귀여운 고래 를 보 셨 습 니까?네,바로 docker 입 니 다.Docker 안 은 완전한 운영 체제 이 고 Linux 시스템 입 니 다!
자,초보 튜 토리 얼 C>자바 튜 토리 얼 C>마음대로 실행 인 스 턴 스 를 찾 아 들 어가 서 원래 의 코드 를 삭제 하고 이 코드 를 복사 하여 실행 합 니 다.출력 결과!헤헤
在这里插入图片描述
주의:
일부 온라인 코드 실행 은 일부 가방 을 차단 한 것 이기 때문에 어떤 것 은 반드시 성공 할 수 있 는 것 이 아니다.만약 에 여기 서 작가 가 온라인 코드 제출 집행 에 대해 그런 제한 을 했다 면 우 리 는 리 눅 스 시스템 에 가서 성실 하 게 실행 할 수 밖 에 없다.
하지만 자 이언 트 의 어깨 위 에 서 있 을 때 가 있어 요.정말 편 해 요!
이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기