https://www.npmjs.com/package/graphql-upload
graphql-upload
Middleware and an Upload scalar to add support for GraphQL multipart requests (file uploads via queries and mutations) to various Node.js GraphQL servers.. Latest version: 16.0.2, last published: a year ago. Start using graphql-upload in your project by ru
www.npmjs.com
"graphql-upload": "^13.0.0"
"@types/graphql-upload": "^8.0.12"
13버전으로 패키지 설치 → 14버전 이상일 경우 패키지가 .mjs로 만들어져있어서 ts.config설정을 따로 하지 않는 이상 경로 에러 발생
app.use(graphqlUploadExpress());
Nest.js에 해당 설정 추가해야 파일 업로드 (Multipart/form-data 및 파일 스트림 전송 가능)
import { InputType, Field } from '@nestjs/graphql';
import { FileUpload, GraphQLUpload } from 'graphql-upload';
@InputType()
export class FileInput {
@Field(() => String, { description: '첨부 파일명' })
file_name: string;
@Field(() => String, { description: '첨부 파일 타입' })
file_content_type: string;
@Field(() => GraphQLUpload, { description: '첨부 파일 blob' })
file_data: Promise<FileUpload>;
}
GraphQL Mapping Input
const data = await fileInput.file_data;
const mimetype = data.mimetype;
const fileName = data.filename;
const fileStream = data.createReadStream();
const chunks = [];
for await (const chunk of fileStream) {
chunks.push(chunk);
}
const fileBuffer = Buffer.concat(chunks);
비즈니스 로직
const [formData, setFormData] = useState(new FormData());
const changeFile = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files.length > 0) {
const fileFormData = new FormData();
const file = event.target.files?.[0];
/** GraphQL 쿼리와 변수를 formData에 추가 **/
fileFormData.append(
'operations',
JSON.stringify({
query: `
mutation fileUpload($fileInput: FileInput!) {
fileUpload(fileInput: $fileInput)
}
`,
variables: {
fileInput: {
file_name: file.name,
file_content_type: file.type,
},
},
}),
);
/** 이진 데이터 매핑 **/
fileFormData.append(
'map',
JSON.stringify({ '0': ['variables.fileInput.file_data'] }),
);
/** 이진 데이터 매핑 **/
fileFormData.append('0', file);
setFormData(fileFormData);
}
};
const upload = useCallback(() => {
fetch('http://localhost:4000/graphql', {
method: 'POST',
headers: {
'x-apollo-operation-name': 'UploadFile', // application/json외 CSRF 방지 허용 Header
},
body: formData,
})
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error));
}, [formData]);
<input type="file" onChange={changeFile} />
<button onClick={() => upload()}>Upload</button>
클라이언트에서 호출할 때 mutlipart/form-data 형식 으로 보내야한다
이진 데이터의 경우 따로 매핑이 필요하다 → GraphQL의 경우 applicaiton/json으로만 통신하게 되어있기 때문에 나머지는 막아놨다 허용하게 하려면 Header에 값을 붙여서 보내야 mutlipart/form-data도 허용 이 된다
자세한 내용들은 아래 참고 및 출처를 확인 하길 바랍니다
!!!
근데 해보니까 이진데이터를 보내서 받아 DB에 저장까지는 되었다만 DB 이진 데이터를 다시 GraphQL로 return이 안 된다 GraphQL은 application/json 타입으로만 return하는 형태로 만들어져있기 때문에 Response 객체를 얻어서 조작하는 행위가 안 됨 → Response에 Buffer를 담고 어떤 파일 형식인지 Header에 보내면 호출 시 해당 파일 보내질텐데 그게 안 됨 이럴바에 그냥 base64로 보내고 base64로 받자
🔗 참고 및 출처
https://medium.com/prisma-korea/graphql-%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1-b41c5f4deca0
https://graphql-compose.github.io/docs/guide/file-uploads.html
https://www.apollographql.com/docs/apollo-server/v3/data/file-uploads/