[ Flutter + Express.js + MongoDB ] 파일 업로드(File Upload) - 2

SEUNGHWANLEE·2021년 5월 20일
0

TIL

목록 보기
6/9
post-thumbnail

post 기능까지 구현을 마치고 읽어오는 get 단계입니다. post로 등록된 document에는 imagePath란 필드에 files collectionfilename과 같은 값을 갖고 있습니다. 이점을 이용해서 article에는 binary로 된 이미지 파일이 존재하지 않기 때문에, imagePath란 필드를 사용해서 files 그리고 chunks에서 데이터를 가져오는 단계를 거쳐야합니다.

해야할 일

  1. article에서 imagePath의 값을 가져와 files collection에서 검색
  2. files 에서 검색된 값으로 chunks에서 검색
  3. 검색된 binary/buffer 를 반환될 객체에 추가

이렇게 3단계를 거쳐서 response값을 전달해주려 합니다.

파일 읽어오기 - (1) Express.js

service

저장되어있는 모든 document를 읽어오는 기능으로 readAll()을 구현하였습니다.

// read all
    readAll = async () => {
        try {
            // db query
            const res = await Article.find();
            var updatedRes = []
            // get Image
            async function updateImageProperty(res, updatedRes) {
                // iterable
                for (const document of res) {
                    const { imagePath } = document;
                    if (imagePath != null) {
                        const searchImageFile = await ArticleImage.findOne({ filename: imagePath });
                        if (searchImageFile != null) {
                            // get chunks
                            const searchImageChunk = await ArticleImageChunk.find({ files_id: searchImageFile._id });
                            var buffer = '';
                            searchImageChunk.map((doc) => buffer += Buffer.from(doc.data, 'binary').toString('base64'));
                            // put 'image' key and value
                            document['image'] = buffer;
                            updatedRes.push(document);
                            // console.log(JSON.stringify(updatedRes));
                            
                        }
                    } else {
                        updatedRes.push(document);
                    }
                }

                return updatedRes;
            }
            const response = await updateImageProperty(res, updatedRes);
            if (!response) return onGetNotFound;
            return onGetSuccess(updatedRes);
        } catch (error) {
            console.error(error);
            return onGetFailure(err);
        }
    }
  1. 우선 article collection에 있는 모든 값을 읽어와 res에 저장합니다.
  2. updatedResclient에게 보낼 return(최종 response) 값입니다.
  3. async (비동기) 작업을 할 때, forEach를 사용하지 않고 for .. of for loop 를 사용합니다.
    forEach는 반복문을 돌면서 function내의 내용만을 처리하기 때문에, 병렬로 순차 실행하는 것은 for loop를 이용해야합니다. forEach는 반복문이 종료되기까지 기다려주지 않습니다.
  4. article-images.files내에서 해당 documentimagePath 값과 같은 값을 검색합니다.
  5. null check를 해준 다음, article-images.chunks 내에 files_id와 files의 ObjectId값이 같은 값을 검색합니다.
    이때, chunks안에 파일이 크기가 커서 분할 저장된 경우에는 n:0, n:1 이런식으로 분리가 되어있기 때문에 map 함수를 통해서 buffer에 더해주었습니다. 그리고 더해준 buffer를 document 내 image 필드 값으로 overwrite해줍니다.
    만약 이미지 및 파일이 없는 경우에는 별도의 절차 없이 리스트에 추가를 해주고 마지막에 같이 response로 전달해줍니다.

파일 읽어오기 - (2) Flutter

서버에서 전달되는 값을 저는 아래와 같이 설정해두었습니다.

var onGetSuccess = (body) => {
    return {
        status: 200,
        result: {
            message: "[GET] msg received",
            result: body
        }
    };
}

따라서 client로 전달되는 값은 result 로 조회가 가능합니다. result 내에는 List 타입으로 반환되어 전달되기 때문에 적절한 parsing 작업을 아래와 같이 해주었습니다.

// GET - ALL
  Future<List<Article>> findAll() async {
    final res = await http.get(baseURL);
    if (res.statusCode == 200) {
      print('status 200');
      final parsedJson = json.decode(res.body)['result'] as List;
      final list = parsedJson.map((json) => Article.fromJson(json)).toList();
      return list;
    } else {
      throw Exception(res.body.toString());
    }
  }

추가로 이미지를 처리할때는 Image.memory(Uri.parse(base64).data.contentAsBytes())처럼 구현하였습니다. 해당코드는 아래 코드와 같습니다.

class ImageWrapper extends StatefulWidget {
  final String image;
  ImageWrapper({this.image});
  
  _ImageWrapperState createState() => _ImageWrapperState();
}

class _ImageWrapperState extends State<ImageWrapper> {
  
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return ClipRRect(
      borderRadius: BorderRadius.circular(8),
      child: Container(
        child: Align(
          alignment: Alignment.center,
          widthFactor: 0.8,
          heightFactor: 0.8,
          child: Image.memory(
            base64Decode(widget.image),
            width: size.width * 0.27,
            height: size.width * 0.27,
            alignment: Alignment.center,
            errorBuilder: (context, error, stackTrace) => Icon(
                Icons.error_outline_rounded,
                size: 24,
                color: themeGrayText),
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}


위에 화면처럼 왼쪽에는 이미지 파일에 대한 buffer를 출력해보고 오른쪽에는 에뮬레이터를 통해서 값을 출력해보았습니다.

profile
잡동사니 😁

0개의 댓글