
Goで簡単に実装!パスワード付きZIPでデータ保護を強化
この記事では、Goでパスワード保護されたZIPファイルを作成する方法をご紹介します。具体的には、ユーザーがファイルをアップロードし、サーバー側でそれを ZIP ファイルに圧縮する実装をみていきます。
ファイルアップロード:ユーザーがファイルをアップロード
Goを使用したファイル圧縮:Goライブラリを使用してファイルをZIP形式に圧縮
パスワード保護の追加:機密データを保護するため、ZIPファイルにパスワードを設定
前提条件
Goは標準ライブラリを通じてZIPファイルの作成をネイティブにサポートしています。しかし、ZIPアーカイブにパスワード保護を追加してセキュリティを強化したい場合は、サードパーティのパッケージを活用する必要があります。ここでは、パスワード保護されたZIPファイルの操作機能を提供するgithub.com/alexmullins/zip パッケージを使用します。
go get -u github.com/alexmullins/zip
また、API を実装するために、Gin Web フレームワークを使用します。
go get -u github.com/gin-gonic/gin
コードの実装
まずは、ファイルのアップロードと標準的なZIP圧縮プロセスを処理する基本的なGinアプリケーションを作成しましょう。
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// Initialize Gin router
r := gin.Default()
// Route for uploading files
r.POST("/upload",func(c *gin.Context){
// Retrieve the uploaded file
file, err := c.FormFile("file")
// Handle errors...
// Open the uploaded file
uploadedFile, err := file.Open()
// Handle errors...
defer uploadedFile.Close()
// Create a buffer to write the zip file to
buf := new(bytes.Buffer)
// Zip the uploaded file
err = zipFile(buf, file.Filename, uploadedFile)
//send to user or upload to storage service
})
r.Run(":8080")
}
func zipFiles(buf *bytes.Buffer, filename string, file io.Reader) error {
//handle zipping process
}
上記は、ユーザーがファイルをアップロードするための基本的なルートを持つ Gin のコードです。ファイルの取得し、開き、ZIP圧縮プロセス後のクローズなど、一連の処理を行います。
ここでは、処理されたZIPファイルを保存するためにバッファを使用し、直接ユーザーに送信していますが、必要に応じてストレージサービスにアップロードすることもできます。
では、ZIP ファイルを作成する処理をみていきましょう。これを実現するため、Go の archive/zip パッケージを使用します。
func zipFiles(buf *bytes.Buffer, filename string, file io.Reader) error {
zipWriter := zip.NewWriter(buf)
fileInfo, err := zipWriter.Create(filename)
// Handle errors...
_, err = io.Copy(fileInfo, file)
// Handle errors...
err = zipWriter.Close()
// Handle errors...
return nil
}
コードを詳しく見ていきましょう。
まず、zip.NewWriter(buf) を使用して新しい zipWriter を作成します。これにより、提供されたバッファにZIPファイルを書き込む新しいZIPアーカイブライターが初期化されます。
次に、zipWriter.Create(filename)を使用してZIPアーカイブ内に新しいファイルを作成します。このメソッドは、ファイルの内容を書き込むことができるio.WriterとしてfileInfoを返します。filenameはZIPアーカイブ内のファイル名です。fileInfoに書き込まれた内容は、指定されたファイル名でZIPアーカイブに追加されます。
その後、io.Copyを使用して、元のファイル(file)の内容をZIPアーカイブ内のファイル(fileInfo)にコピーします。これにより、元のファイルの内容が効果的にZIPアーカイブに追加されます。
ファイルをZIPアーカイブに追加した後、zipWriter.Close()を使用してzipWriterを閉じます。これにより、必要なすべてのデータがフラッシュされ、バッファに書き込まれることが保証されます。クローズ中に発生したエラーはerr変数に割り当てられます。
コンテンツのメタ情報(タイムスタンプ、圧縮方法、圧縮サイズ、またはコンテンツに関する追加情報など)を変更したい場合、ファイル名のみを受け付ける zip writer の Create メソッドを使用する代わりに、修正されたヘッダーを持つ CreateHeader メソッドを使用できることです。
header := &zip.FileHeader{
Name: filename, // Name of the file within the zip archive
method: zip.Deflate
// Other attributes...
}
zipWriter := zip.NewWriter(buf)
// Create a file within the zip archive with custom header
fileInfo, err := zipWriter.CreateHeader(header)
// Handle errors...
// Continue
すべてのプロセスの後、実際のコードは次のようになります。
package main
import (
"archive/zip"
"bytes"
"github.com/gin-gonic/gin"
"io"
"net/http"
)
func main() {
// Initialize Gin router
router := gin.Default()
// Define a route to handle file uploads
router.POST("/upload", handleFileUpload)
// Start the server
router.Run(":8080")
}
func handleFileUpload(c *gin.Context) {
// Retrieve the uploaded file
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Open the uploaded file
uploadedFile, err := file.Open()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to open file"})
return
}
defer uploadedFile.Close()
// Create a buffer to write the zip file to
buf := new(bytes.Buffer)
// Zip the uploaded file
err = zipFiles(buf, file.Filename, uploadedFile)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to zip file"})
return
}
// Set the response headers
c.Header("Content-Description", "File Transfer")
c.Header("Content-Disposition", "attachment; filename="+file.Filename+".zip")
c.Header("Content-Type", "application/zip")
c.Header("Content-Length", string(buf.Len()))
// Send the zip file back to the user
c.Data(http.StatusOK, "application/zip", buf.Bytes())
}
func zipFiles(buf *bytes.Buffer, filename string, file io.Reader) error {
// Create a new zip archive
zipWriter := zip.NewWriter(buf)
// Add the file to the zip archive
fileInfo, err := zipWriter.Create(filename)
if err != nil {
return err
}
// Copy the file data to the zip archive
_, err = io.Copy(fileInfo, file)
if err != nil {
return err
}
// Close the zip writer
err = zipWriter.Close()
if err != nil {
return err
}
return nil
}
ZIPにパスワードを追加する
Go標準アーカイブはアーカイブファイルにパスワードを追加する機能を提供していないため、サードパーティライブラリの alexmullins/zip を使用します。このパッケージは標準パッケージの上に書かれているため、ZIP圧縮プロセスは標準ライブラリと同じですが、パスワードを追加する追加機能があります。
まず、上記のコードで、"archive/zip" を "github.com/alexmullins/zip" に変更します。ZIPファイルにパスワードを追加するために、通常の標準ライブラリでは存在しない zipWriter の Encrypt メソッドを使用します。
func zipFiles(buf *bytes.Buffer, filename string, file io.Reader) error {
zipWriter := zip.NewWriter(buf)
fileInfo, err := zipWriter.Encrypt(filename,"secret")
// Handle errors...
_, err = io.Copy(fileInfo, file)
// Handle errors...
err = zipWriter.Close()
// Handle errors...
return nil
}
以上です。少しでも参考になれば幸いです。
この記事は、2024年10月に弊社のエンジニア Rajan Sapkota が執筆した内容を日本語に翻訳したものです。
英語版はこちらをご覧ください。
https://articles.wesionary.team/file-zipping-with-password-protection-using-go-e3115f3baf5c
採用情報
私たちはプロダクト共創の仕組み化に取り組んでいます。プロダクト共創をリードするプロダクト・マネージャー、そして、私たちのビジョンを市場に届ける営業メンバーを募集しています!
開発パートナーをお探しの企業様へ
弊社は、グローバル開発のメリットを活かし、高い費用対効果と品質を両立しています。経験豊富で多様性のあるチームが、課題を正しく理解し、最適なシステムと優れた体験を実現します。業務システムの開発、新規事業の開発、業務効率化やDX化に関するお困りごと、ぜひ弊社にご相談ください。