Reactでxmlファイルをアップロードして使う

use-file-uploadを使ってXMLファイルをアップロードしてパースする手順のメモです

# use-file-upload

今回はuse-file-uploadを使用しました
https://www.npmjs.com/package/use-file-upload

use-file-uploadはReactフックとして動作するライブラリで、useFileUpload()でstateを作っておいて、buttonなどからonclickで呼ぶ事でアップロードしたファイルをstateとして扱えるようにしてくれます。

以下の例のsource, name, size, fileはそれぞれblob url, ファイル名、ファイルサイズ、ファイルオブジェクトになります。画像ファイルをアップロードして表示する場合は、blob urlであるsourceをimgタグのsrcに与える事でサーバー上のファイルと同様に表示できます。

```javascript:src/app.js
import React from 'react'
import { useFileUpload } from 'use-file-upload'
 
const App = () => {
  const [file, selectFile] = useFileUpload()
 
  return (
    <div>
      <button
        onClick={() => {
          // Single File Upload
          selectFile({}, ({ source, name, size, file }) => {
            // file - is the raw File Object
            console.log({ source, name, size, file })
            // Todo: Upload to cloud.
          })
        }}
      >
        Click to Upload
      </button>
 
      {file ? (
        <div>
          <img src={file.source} alt='preview' />
          <span> Name: {file.name} </span>
          <span> Size: {file.size} </span>
        </div>
      ) : (
        <span>No file selected</span>
      )}
    </div>
  )
}
 
export default App;
```

上記の例ではbuttonタグにCSSを設定せずに表示するので以下のようなシンプルなボタンが表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/a7882bdb-707d-3f33-cf86-7659d63d743e.png)


# アップロードしたXMLファイルをオブジェクトに変換


fileオブジェクトを直接触るやり方が分からなかったので、blob urlをXMLHttpRequestで読んでくるやり方でやりました。XMLHttpRequestでblobを読みに行ってresponseXMLでXMLとして取得します。XMLとして読めなかった場合はresponseXMLにnullが入りますので、nullでなかった場合は表示するというようにしました。

```javascript
import React from 'react'
import { useFileUpload } from 'use-file-upload'

function applyGPX(xml){
  console.log(xml);
};

function getXML(source) {
  console.log('source:');
  console.log(source);

  var xhr = new XMLHttpRequest();
  xhr.onload = function(e){
    const xml = xhr.responseXML;
    if (xml != null){
      applyGPX(xml);
    } else {
      console.log('not xml file');
    }
  };
  xhr.open("GET", source);
  xhr.send();
};
 
const App = () => {
  const [file, selectFile] = useFileUpload()
 
  return (
    <div>
      <button
        onClick={() => {
          selectFile({}, ({ source, name, size, file }) => {
            getXML(source);
          })
        }}
      >
        Click to Upload
      </button>
 
      {file ? (
        <div>
          <span> Name: {file.name} </span>
          <span> Size: {file.size} </span>
        </div>
      ) : (
        <span>No file selected</span>
      )}
    </div>
  )
};
 
export default App;
```

# xmlオブジェクトからほしい情報を拾う

XMLオブジェクトが作れたのでほしい情報を拾っていこうと思います。XMLHttpRequest.responseXMLで取得したオブジェクトはgetElementsByTagName(), getElementById(), getElementsByClassName()などで取得できます。

```javascript:tag名で探す
const elements = hoge.getElementsByTagName('fuga')
```

```javascript:ID名で探す
const elements = hoge.getElementById('fuga')
```

```javascript:class名で探す
const elements = hoge.getElementsByClassName('fuga')
```

また、取得したelementのattributeは以下のように取得できます

```javascript:attributeの取得
const attribute = elements[0].getAttribute('foo')
```

今回はgpxファイルを読み込むので以下のようにして緯度、経度、高度を取得しました。

```javascript

function getTracks(xml){
  console.log('getTracks');
  const trackTags = xml.getElementsByTagName('trkpt');
  console.log('track length: ' + trackTags.length);

  var tracks = [];
  var elevations = [];
  for(let k = 0; k < trackTags.length; k++) {
    let element = trackTags[k];
    let latitude = parseFloat(element.getAttribute('lat'));
    let longitude = parseFloat(element.getAttribute('lon'));
    tracks.push([latitude, longitude]);

    let element2 = element.getElementsByTagName('ele')[0]
    let elevation = parseFloat(element2.innerHTML)
    elevations.push(elevation);
  };
  console.log([tracks, elevations]);
  return [tracks, elevations]
```


# 余談

use-file-upload以外にもいくつかReact用のアップロードライブラリを試したんですが、blobぐらい分かるよね？的なノリで書いてあって地味に苦戦しました。ウェブ関係のライブラリ制作者が常識だから省いても良いと思っている部分を勉強できる本があったら読みたいので、ご存知の方がいらっしゃいましたら教えて下さい。