Databricksファイルシステム(DBFS)上のファイルを圧縮してダウンロードする

なにかしらの処理の結果、生成される複数のファイルを簡単にダウンロードできるように圧縮を行い、zipファイルへのリンクを表示するサンプルです。

サンプルノートブックはこちらです。

https://github.com/taka-yayoi/public_repo/tree/main/dbfs_zip_download

**参考資料**
- [Databricksユーティリティ \- Qiita](https://qiita.com/taka_yayoi/items/3717623187859809515d)
- [Databricksにおけるzipファイルの取り扱い \- Qiita](https://qiita.com/taka_yayoi/items/0197d5c985089255f16a)

# ファイルの準備

ダミーデータを準備します。以下のパスは適宜変更してください。

```py:Python
# 処理の結果が格納されるパスです
output_file_path = "/tmp/takaaki.yayoi@databricks.com/processed"

# ブラウザからダウンロードできるようにFileStore配下のパスとします
download_file_path = "/FileStore/shared_upload/takaaki.yayoi@databricks.com/viz_results"
```

現在時刻を用いてフォルダを作成します。

```py:Python
# datetimeモジュールを使用
import datetime

t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
#print(repr(now))

# YYYYMMDDhhmmss形式に書式化
directory_name = now.strftime('%Y%m%d-%H%M%S')
#print(d)

target_path = f"{output_file_path}/{directory_name}"
print("target path:", target_path)

# 日付の入ったディレクトリを作成
dbutils.fs.mkdirs(target_path)
```
![Screen Shot 2022-08-09 at 15.31.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/14709151-e64b-afbf-064b-e8b624f05ccd.png)

ワインデータセットをコピーします。

```py:Python
dbutils.fs.cp("dbfs:/databricks-datasets/wine-quality/winequality-red.csv", target_path)
dbutils.fs.cp("dbfs:/databricks-datasets/wine-quality/winequality-white.csv", target_path)
dbutils.fs.cp("dbfs:/databricks-datasets/wine-quality/README.md", target_path)
```

```py:Python
# ファイルが格納されていることを確認
display(dbutils.fs.ls(target_path))
```
![Screen Shot 2022-08-09 at 15.32.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/352f442d-0397-5ea7-cc40-bba76e764421.png)

# ファイル圧縮

DBFSの実態はオブジェクトストレージなので、ランダムアクセスが必要となるファイル圧縮をその場で行うことができません。このため、クラスターのローカルストレージに一旦コピーして圧縮を行う必要があります。

`dbutils.fs`でパスを指定する際にローカルストレージを参照するには、パスの先頭に`file:`をつけます。正直ここは分かりにくいところだと思います。

[こちら](https://qiita.com/taka_yayoi/items/897264c486e179d72247#dbfs%E3%81%A8%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%83%BC%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E3%83%91%E3%82%B9)で触れているように、使っているAPIによってデフォルトでDBFS(分散ファイルシステム)にアクセスするのか、ローカルファイルシステムにアクセスするのかが異なります。このデフォルトの挙動を変えるために`file:`や`/dbfs`を使います。

- Spark API、`%fs`、`dbutils.fs`などは、デフォルトのアクセス先はDBFSになります。
- 一方、pandasなどのシングルマシン向けPythonライブラリ(ローカルファイルシステムAPI)、`%sh`などは、デフォルトのアクセス先はローカルファイルシステムになります。

以下で使用している`shutil`ではファイルの圧縮を行いますが、上述の通り、DBFS上に直接圧縮ファイルを作成することができません。このため、DBFSからローカルファイルシステムにファイルをコピーしています。この際に`dbutils.fs.cp`を使っています。これのデフォルトのアクセス先はDBFSとなるので、ここでローカルファイルシステムを参照するには、パスの先頭に`file:`を追加します。

なお、ローカルファイルシステムAPIでDBFSを参照するには、パスの先頭に`/dbfs`を追加します。以下の図にまとめていますが、これでも正直分かりにくいと思います。まずは以下の2点を覚えていただくといいのではと思います。

:::note info
**パスのプレフィックス**
- pandasでローカルファイルシステムにアクセスするにはパスはそのまま、DBFSにアクセスするにはパスの先頭に`/dbfs`を追加します。
- Spark APIや`%fs`でDBFSにアクセスするにはパスはそのまま、ローカルファイルシステムにアクセスするにはパスの先頭に`file:`を追加します。
:::

![](https://camo.qiitausercontent.com/818f907f57c41850f9d3ac6f6092ad909b7c3121/68747470733a2f2f646f63732e64617461627269636b732e636f6d2f5f696d616765732f646266732d616e642d6c6f63616c2d66696c652d70617468732e706e67)


**参考資料**
- [Databricksファイルシステム\(DBFS\) \- Qiita](https://qiita.com/taka_yayoi/items/897264c486e179d72247)
- [Databricksにおけるファイルシステム \- Qiita](https://qiita.com/taka_yayoi/items/e16c7272a7feb5ec9a92)
- [Databricksを使い始めたときに感じる疑問 \+ ベストプラクティス \- Qiita](https://qiita.com/taka_yayoi/items/8718c7c7d922e6f942bc)

```py:Python
# DBFSからローカルストレージにコピー
dbutils.fs.cp(target_path, f"file:/tmp/{directory_name}", True)
```

```py:Python
# ローカルストレージを参照
display(dbutils.fs.ls(f"file:/tmp/{directory_name}"))
```
![Screen Shot 2022-08-09 at 15.32.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cb0f35a1-9180-49ec-a4e1-dd50b0c1c379.png)

[shutil](https://docs.python.org/3/library/shutil.html)を使って圧縮を行います。

- [PythonでZIPファイルを圧縮・解凍するzipfile \| note\.nkmk\.me](https://note.nkmk.me/python-zipfile/)

```py:Python
# ディレクトリを圧縮
import shutil
shutil.make_archive(f"/tmp/{directory_name}", format='zip', root_dir=f'/tmp/{directory_name}')
```
![Screen Shot 2022-08-09 at 15.33.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/35e51557-a014-d4d6-080f-b3a7abc4ff95.png)

ブラウザからダウンロードできるように、DBFSの`/FileStore`にファイルをコピーします。

```py:Python
dbutils.fs.cp(f"file:/tmp/{directory_name}.zip", f"{download_file_path}/{directory_name}.zip")
```

```py:Python
display(dbutils.fs.ls(download_file_path))
```
![Screen Shot 2022-08-09 at 15.34.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/3050f531-f176-ccb6-77e6-702233a878ac.png)

# ダウンロードリンクの生成

DBFSの[FileStore](https://qiita.com/taka_yayoi/items/01d9b69d2f5283d27d96)配下のファイルには、ブラウザから`/files`のURLを指定することでアクセスすることができます。

```py:Python
import re

# FileStoreをfilesに置き換えます
download_url_path = re.sub("FileStore", "files", download_file_path)
print(download_url_path)
```

`displayHTML`関数を使ってリンクをノートブック上にレンダリングします。

```py:Python
displayHTML (f"""
<a href='{download_url_path}/{directory_name}.zip'>{directory_name}.zip</a>
""")
```
![Screen Shot 2022-08-09 at 15.35.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/0ee3fb60-7a84-10a6-01b9-11ed60754048.png)

このリンクをクリックするとzipファイルがローカルにダウンロードされます。これを解凍すると、上で圧縮した3つのファイルを手に入れることができます。
![Screen Shot 2022-08-09 at 15.36.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/97f568be-7e02-2644-51c3-dbfc4ff37200.png)

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)