인프런 커뮤니티 질문&답변

ststxx님의 프로필 이미지
ststxx

작성한 질문수

[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!

StreamSubscription listen 내부에서 file copy 이슈

작성

·

164

·

수정됨

0

안녕하세요. 학습내용을 확장하는 중 이슈가 있어서 문의드립니다.

윈도우 OS 타겟으로 개발 중이며,

특정폴더(origin)에 파일이 생성되는 event 발생 시 다른 특정폴더(target)로 파일을 복사하는 로직을 구현했습니다.

이슈는, 용량이 1kb로 작은 파일일 경우 잘 작동하지만, 용량이 몇mb 정도로 큰파일은 아래와 같은 예외가 발생하며 복사가 되지 않습니다.

예외는 다음과 같습니다.

Unhandled Exception: PathAccessException: Cannot copy file to 'C:\0.st\target\10.tdms', path = 'C:\0.st\origin\10.tdms' (OS Error: 다른 프로세스가 파일을 사용 중이기 때문에 프로세스가 액세스 할 수 없습니다.

, errno = 32)

#0      _checkForErrorResponse (dart:io/common.dart:55:9)

#1      File.copy.<anonymous closure> (dart:io/fileimpl.dart:356:7)

<asynchronous suspension>

 제가 파일을 복사하기 위해 구현한 함수는 아래와 같습니다.

중간에 newOriginFile.copy(copyFilePath); 부분에서 예외가 발생합니다.

참고로 copySync 메서드로 변경해 보았으나 같은 현상이 발생합니다.

복사할 대상 파일을 다른 프로세스에서 열지 않은 상태인데도 위와 같은 예외가 발생하여 디버깅이 안되는 상황입니다.

항상 감사합니다!

void _startEvaluationMode() {
    Navigator.pop(context, 'OK');
    // 계측 모드 시작
    setState(() {
      _evaluationMode = true;
    });
    // origin 경로 불러오기
    if (_directoryInfoBox.containsKey(DirectoryType.origin.name)) {
      setState(() {
        _originDirectory =
            Directory(_directoryInfoBox.get(DirectoryType.origin.name)!.path);
      });
      if (!_originDirectory!.existsSync()) {
        // 실제 origin 경로의 폴더가 존재하지 않는 상태
        // origin directory 확인하라는 alert dialog 보여주고 evaluation mode 종료
      }
      // origin 경로 워칭
      Stream<FileSystemEvent> originDirFileEvent = _originDirectory!.watch();
      print('start evaluation mode');

      // _streamListner = originDirFileEvent.listen((event) async {
      //   if (event.type == 1) {
      //     print('create event');
      //   }
      // });

      _streamListner = originDirFileEvent.listen((event) {
        // origin 경로에 새 파일이 생성된 경우 FileSystemCreateEvent type == 1
        if (event.type == 1) {
          print('created file: ${event.path}');
          final newOriginFile = File(event.path);

          // 포맷에 맞는 파일 이름 생성
          var newOriginFilename = p.basenameWithoutExtension(event.path);
          // print('base file name: ${newOriginFilename}');

          String testMethodRoadName =
              TestMethodRoadInfoManager.getNameById(newOriginFilename);
          // 중복되는 이름 확인
          // 파일 이름을 _ 로 분리하여 4번째 인덱스 값으로 구분
          List<String> existingTestMethodRoadNameList =
              _evaluationItem!.evaluationFileInfos!.map((fileInfo) {
            return p
                .basenameWithoutExtension(fileInfo.evaluationFilePath)
                .split('_')
                .first;
          }).toList();
          // print(existingTestMethodRoadNameList);
          //
          List<String> duplicatedList =
              existingTestMethodRoadNameList.where((name) {
            return name == testMethodRoadName;
          }).toList();
          String fileNameWithDuplicateCount = '';
          if (duplicatedList.isNotEmpty) {
            fileNameWithDuplicateCount =
                '${testMethodRoadName}_${duplicatedList.length + 1}';
          } else {
            fileNameWithDuplicateCount = '${testMethodRoadName}_1';
          }
          // print('road name: ${testMethodRoadName}');
          // var copyFilePath = p.join(_itemDirectory!.path,
          //     '${_itemFilePrefix}_${fileNameWithDuplicateCount}.tdms');
          var copyFilePath = p.join(
              _itemDirectory!.path, '${fileNameWithDuplicateCount}.tdms');
          print('copy file path: ${copyFilePath}');

          // 파일을 eval item 경로로 이름 바꾸어 복사
          newOriginFile.copy(copyFilePath);
          print('copy finish');

          // eval item DB의 evaluationFileInfos 리스트 업데이트
          var newEvaluationFileInfo = EvaluationFileInfo(
            originFilePath: event.path,
            evaluationFilePath: copyFilePath,
            activated: true,
          );
          setState(() {
            _evaluationItem!.evaluationFileInfos!.add(newEvaluationFileInfo);
          });
          // print(
          //     'file info length: ${_evaluationItem!.evaluationFileInfos!.length}');
          _evaluationItemBox.put(_evaluationItem!.id, _evaluationItem!);

          // ui 상 file list, count 업데이트
          List<File> files = [];
          for (var entity in _itemDirectory!
              .listSync(recursive: false, followLinks: false)) {
            if (entity is File) {
              files.add(entity);
            }
          }
          setState(() {
            _files = files;
          });
        }
      });
    }
  }

답변 1

1

코드팩토리님의 프로필 이미지
코드팩토리
지식공유자

안녕하세요!

의심하시는 것 처럼 파일 크기의 문제가 아닐 가능성이 높습니다. (부가적인 이유일수는 있습니다.)

에러 메세지를 잘 읽어보시면 "다른 프로세스가 파일을 사용 중이기 때문에 프로세스가 액세스 할 수 없습니다."라고 나와있습니다.

말 그대로 이미 열려있는 또는 사용중인 파일을 다시 접근하려고하니 문제가 생기는겁니다.

파일이 큰 경우에만 에러가 난다면 파일의 크기가 작은 경우 동시 접근 문제가 생기기 전에 파일 관련 작업이 끝나기 때문일 수 있습니다.

어찌됐든 모든 IO 관련 작업은 파일 크기와 관련 없이 이미 접근중인 파일은 다시 접근하지 못하도록 코드를 작성하시는게 중요합니다.

감사합니다!

ststxx님의 프로필 이미지
ststxx
질문자

파일이 다른곳에서 접근하는 로직은 없었으나, 일단 말씀하신바와 같이 파일이 열려있다는 에러였기때문에, 타겟 파일이 available한 상태가 될때까지 잠시 기다리는 로직을 추가하여 기능 구현은 했습니다. 왜 어디서 리소스를 잡고 있는지 모르겠으나...일단 해결이 되어 다행입니다.

ststxx님의 프로필 이미지
ststxx

작성한 질문수

질문하기