React + Spring Boot での画像投稿

#はじめに
ReactとSpring Boot　+ Chakra UIでアプリケーション開発を行おうとしており、
その過程で画像投稿機能を組み込む予定なため学んだことを備忘録として記載します。

#React
``` NewUser.tsx
return (
    <>
      <Wrap justify="center" mt={5}>
      <Box 
      w="100vh"
      h="750px"
      bg="white"
      borderRadius="10px"
      shadow="md" p={4}
      _hover={{cursor: "pointer", opacity: 0.8}}
    >
      <Stack textAlign="center" spacing={5} p={4}>
        <FormControl>
          <FormLabel>名前：</FormLabel>
          <Input value={name} onChange={onChangeName}/　>
        </FormControl>
        <FormControl>
          <FormLabel>メールアドレス：</FormLabel>
          <Input value={email} onChange={onChangeEmail}/>
        </FormControl>
        <FormControl>
          <FormLabel>TEL：</FormLabel>
          <Input value={phone} onChange={onChangePhone}/>
        </FormControl>
        <FormControl>
          <FormLabel>ユーザーネーム：</FormLabel>
          <Input value={username} onChange={onChangeUsername}/>
        </FormControl>
        <FormControl>
          <FormLabel>概要：</FormLabel>
          <Input value={description} onChange={onChangeDescription}/>
        </FormControl>
        <FormControl>
          <FormLabel>画像：</FormLabel>
          <Input type="file" accept="image/*,.png,.jpg,.jpeg" onChange={(e: React.ChangeEvent<HTMLInputElement>) => getImage(e)}/>
        </FormControl>
        <FormControl>
          <FormLabel>パスワード：</FormLabel>
          <InputGroup size='md'>
            <Input type={show ? 'text' : 'password'} value={password} onChange={onChangePassword}/>
            <InputRightElement width='4.5rem'>
              <Button h='1.75rem' size='sm' onClick={clickShow} >
                {show ? 'Hide' : 'Show'}
              </Button>
            </InputRightElement>
          </InputGroup>
        </FormControl>
        <Button borderRadius={10} bg="teal.300" onClick={onClickUserPost}>登録</Button>
      </Stack>
    </Box>
    </Wrap>
    </>
  )
```
HTMLの部分はこんな感じ

#画像投稿機能
```NewUser.tsx
const [images, setImages] = useState<File>();

const getImage = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    if(!e.target.files) return
    const img: File = e.target.files[0]
    setImages(img)
  },[])
```

e.target.files[0]でFile型にしたのを変数に入れ、
Stateに格納します。


#Spring側にpost
```NewUser.tsx
const onClickUserPost = () => {
    const data = new FormData()
    data.append('file',images)

    const user = {
      name : name,
      email: email,
      phone: phone,
      username: username,
      description: description,
      password: password
    }

    data.append('user',new Blob([JSON.stringify(user)],{type : 'application/json'}))
    console.log(data)
    axios.post("http://localhost:8080/api/v1/users_manegement/", data)
    .then( () => {
      history.push("/");
    }).catch(() => alert("失敗しました！"));
  }
```

ここで大事なのはFormData()です。
FormDataを詰め込むとmultipart/form-dataとして送れるとのこと。
単純な文字列データ以外も送れるとのことで重宝しそう。
appendする際はキー名とvalueを書く

文字列はJSON文字列に変換後、バイナリデータとしてFormDataに格納しAxiosでpostをすれば終わりです。

::: note info
import時のコマンド
npm install --save form-data
:::

#Spring Boot
```UserController.java
@Operation(summary = "ユーザーを登録します")
	@PostMapping("/")
	public User insert(
            @RequestPart("user") User user,
			@RequestPart("file") MultipartFile file)
            throws IOException {
		
		
		return servise.insert(user, file);
	}
```
@RequestPartでReact側で指定したキー名と受け取るvalueを書けばバイナリーデータを受信できます。

#Base64に変換
```UserServise.java
package dex.itboot.rest.servise;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Base64;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import dex.itboot.rest.form.UserForm;
import dex.itboot.rest.model.User;
import dex.itboot.rest.repository.UserRepository;


@Service
public class UserServise {

	@Autowired
	private UserRepository repository;
	
	public User insert(User user, MultipartFile file) throws IOException {
		
		String fileEntension = null;
		
		try {
			fileEntension = getEntension(file.getOriginalFilename());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		String base64FileString = Base64.getEncoder().encodeToString(file.getBytes());
		if("jpg".equals(fileEntension)) {
			base64FileString = "data:image/jpeg;base64," + base64FileString;
		} else if("png".equals(fileEntension)) {
			base64FileString = "data:image/png;base64," + base64FileString;
		}
		
		user.setImage(base64FileString);
		
		return repository.save(user);
		
	}
	
	private String getEntension(String fileName) throws Exception {
		if(fileName == null) {
			throw new FileNotFoundException();
		}
		
		int point = fileName.lastIndexOf(".");
		if (point == -1) {
			throw new FileNotFoundException();
		}
		return fileName.substring(point + 1);
		
	}
}
```
受け取った画像をBase64(テキストデータ)に変換してDBにINSERTすれば完了。
これで完了！！

#まとめ
画像データをサーバー側に送るには

今回書いたmultipart/form-dataで送る方法と
application/jsonで送る方法があるとのこと。

ただJSONで送ると送信データが増えるとのこと
それぞれメリット・デメリットがあるのでもっと学ばなきゃな...

あとAxiosで送る関数もカスタムフック化したりすると見やすくなるのかなと思います！


以上です！

###参考記事
https://qiita.com/harumaxy/items/035ee46c82e8211d831c

https://qiita.com/wrongwrong/items/3e9b7b04d004ed71d921

https://qiita.com/natuuu0831/items/8b392ad47133b575b620