Go 프로그램에서 정적 리소스를 묶는 가장 좋은 방법은 무엇입니까? [닫은]


100

저는 개발자의 컴퓨터에서 응용 프로그램 / 웹 서비스를 디버그하는 데 도움이되는 도구로 사용되는 Go의 작은 웹 응용 프로그램을 작업 중입니다. 프로그램에 대한 인터페이스는 HTML뿐만 아니라 일부 JavaScript (기능 용), 이미지 및 CSS (스타일링 용)를 포함하는 웹 페이지입니다. 저는이 애플리케이션을 오픈-소싱 할 계획이므로 사용자는 Makefile을 실행하기 만하면 모든 리소스가 필요한 곳으로 이동할 수 있습니다. 그러나 가능한 한 적은 파일 / 종속성을 가진 실행 파일을 간단히 배포 할 수 있기를 바랍니다. HTML / CSS / JS를 실행 파일과 번들로 묶는 좋은 방법이 있습니까? 그러면 사용자가 하나의 파일 만 다운로드하고 걱정하면됩니까?


지금 내 앱에서 정적 파일을 제공하는 것은 다음과 같습니다.

// called via http.ListenAndServe
func switchboard(w http.ResponseWriter, r *http.Request) {

    // snipped dynamic routing...

    // look for static resource
    uri := r.URL.RequestURI()
    if fp, err := os.Open("static" + uri); err == nil {
        defer fp.Close()
        staticHandler(w, r, fp)
        return
    }

    // snipped blackhole route
}

따라서 매우 간단합니다. 요청 된 파일이 내 정적 디렉토리에 있으면 핸들러를 호출하여 파일을 열고 Content-Type제공하기 전에 좋은 설정을 시도합니다 . 제 생각에는 이것이 실제 파일 시스템을 기반으로해야 할 이유가 없다고 생각했습니다. 컴파일 된 리소스가 있으면 요청 URI로 간단히 색인을 생성하고 그대로 제공 할 수 있습니다.

이 작업을 수행하는 좋은 방법이 없거나이 작업을 시도하여 잘못된 나무를 짖는 경우 알려주십시오. 최종 사용자가 관리 할 수있는 파일의 수를 최소화 할 것이라고 생각했습니다.

보다 적절한 태그가있는 경우 , 자유롭게 추가하거나 알려주세요.



저는 사실 오늘도 정확히 같은 질문을 생각했습니다. 내가 살펴볼 수있는 해결책 go generate은 작은 명령 줄 유틸리티 (내 소스 코드와 함께 패키지화 됨)를 사용하여 파일을 []byte코드에 변수로 포함 된 슬라이스 로 변환하는 stringer것입니다 ( blog.golang.org 참조) . / 생성 ).
Ralph

답변:


76

go-bindata 패키지는 당신이 관심을 가질만한 것 같습니다.

https://github.com/go-bindata/go-bindata

이를 통해 정적 파일을 코드에 포함 할 수있는 함수 호출로 변환 할 수 있으며 호출시 파일 콘텐츠의 바이트 조각을 반환합니다.


8
내 경우에이 업 보팅은 이상하게 보이지만 어쨌든 그렇게 할 것입니다. : p 기록을 위해 이것은 패키지가 아니라 명령 줄 도구입니다.
jimt dec

기록을 위해서, 이것이 제가 프로젝트를 진행 한 경로입니다. 어느 시점에서 @jimt는 일을 더 사용자 친화적으로 만들기 위해 몇 가지 새로운 기능을 도입했지만 더 이상 필요한 세분성을 제공하지 않았습니다. 빌드 프로세스 서문) : github.com/jimmysawczuk/go-binary
Jimmy Sawczuk

37

텍스트 파일 포함

텍스트 파일에 대해 이야기하는 경우 소스 코드 자체에 쉽게 삽입 할 수 있습니다. 역 따옴표를 사용하여 string다음과 같이 리터럴 을 선언하십시오 .

const html = `
<html>
<body>Example embedded HTML content.</body>
</html>
`

// Sending it:
w.Write([]byte(html))  // w is an io.Writer

최적화 팁 :

대부분의 경우 리소스를에 쓰기 io.Writer만하면되므로 []byte변환 결과를 저장할 수도 있습니다 .

var html = []byte(`
<html><body>Example...</body></html>
`)

// Sending it:
w.Write(html)  // w is an io.Writer

주의해야 할 사항은 원시 문자열 리터럴에는 역 따옴표 문자 (`)를 포함 할 수 없다는 것입니다. 원시 문자열 리터럴은 해석 된 문자열 리터럴과 달리 시퀀스를 포함 할 수 없으므로 포함하려는 텍스트에 역 따옴표가 포함되어 있으면 다음 예제와 같이 원시 문자열 리터럴을 분리하고 역 따옴표를 해석 된 문자열 리터럴로 연결해야합니다.

var html = `<p>This is a back quote followed by a dot: ` + "`" + `.</p>`

이러한 연결은 컴파일러에 의해 실행되므로 성능에는 영향을주지 않습니다.

바이너리 파일 포함

바이트 슬라이스로 저장

바이너리 파일 (예 : 이미지)의 경우 (결과 네이티브 바이너리와 관련하여) 가장 간결하고 가장 효율적인 것은 []byte소스 코드에 파일의 내용을 포함하는 것 입니다. 이는 go-bindata 와 같은 타사 도구 / 라이브러리에서 생성 할 수 있습니다 .

이를 위해 타사 라이브러리를 사용하지 않으려는 경우 바이너리 파일을 읽고 파일 []byte의 정확한 콘텐츠로 초기화 될 유형의 변수를 선언하는 Go 소스 코드를 출력하는 간단한 코드 스 니펫 이 있습니다.

imgdata, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}

fmt.Print("var imgdata = []byte{")
for i, v := range imgdata {
    if i > 0 {
        fmt.Print(", ")
    }
    fmt.Print(v)
}
fmt.Println("}")

파일에 0에서 16까지의 바이트가 포함 된 경우 출력 예 ( Go Playground 에서 시도 ) :

var imgdata = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

base64로 저장 string

파일이 "너무 크지"않은 경우 (대부분의 이미지 / 아이콘이 적합 함) 다른 실행 가능한 옵션도 있습니다. 파일의 내용을 Base64로 변환하고 string소스 코드에 저장할 수 있습니다. 응용 프로그램을 시작할 func init()때 ( ) 또는 필요할 때 원본 []byte콘텐츠로 디코딩 할 수 있습니다 . Go는 encoding/base64패키지 에서 Base64 인코딩을 훌륭하게 지원 합니다.

(이진) 파일을 base64 string로 변환하는 것은 다음과 같이 간단합니다.

data, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}
fmt.Println(base64.StdEncoding.EncodeToString(data))

결과 base64 문자열을 소스 코드에 저장합니다 (예 : const.

디코딩은 단지 하나의 함수 호출입니다.

const imgBase64 = "<insert base64 string here>"

data, err := base64.StdEncoding.DecodeString(imgBase64) // data is of type []byte

인용 된대로 저장 string

base64로 저장하는 것보다 더 효율적이지만 이진 데이터 의 인용 된 문자열 리터럴을 저장하는 것이 소스 코드에서 더 길 수 있습니다 . strconv.Quote()함수를 사용하여 문자열의 인용 된 형식을 얻을 수 있습니다 .

data, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}
fmt.Println(strconv.Quote(string(data))

0에서 64까지의 값을 포함하는 바이너리 데이터의 경우 다음과 같이 출력이 표시됩니다 ( Go Playground 에서 시도해보세요 ).

"\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?"

( strconv.Quote()따옴표 를 추가하고 앞에 추가합니다.)

이 따옴표로 묶인 문자열을 소스 코드에 직접 사용할 수 있습니다. 예를 들면 다음과 같습니다.

const imgdata = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?"

사용할 준비가되었으며 디코딩 할 필요가 없습니다. 인용 해제는 컴파일 타임에 Go 컴파일러에 의해 수행됩니다.

다음과 같이 필요한 경우 바이트 슬라이스로 저장할 수도 있습니다.

var imgdata = []byte("\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?")

sh파일을 go 실행 파일에 바인딩하는 방법이 있습니까?
Kasun Siyambalapitiya 2011

데이터는 "바이트 슬라이스로 저장"섹션 아래의 첫 번째 코드 스 니펫에서 imgdata 여야한다고 생각합니다.
논리 x 2

1
@deusexmachina 당신이 맞아요, 고쳤습니다. 놀이터의 코드는 이미 정확했습니다.
icza

2

또한 이국적인 방법이 있습니다. Maven 플러그인 을 사용하여 GoLang 프로젝트를 빌드하고 JCP 전처리기를 사용 하여 바이너리 블록과 텍스트 파일을 소스에 포함 할 수 있습니다. 경우 코드는 아래 줄과 같습니다 ( 여기에서 몇 가지 예를 찾을 수 있습니다 )

var imageArray = []uint8{/*$binfile("./image.png","uint8[]")$*/}

그것은 갖는 디렉토리를 결합 할 수 @is sh또는 실행 등의 이상
Kasun Siyambalapitiya

@KasunSiyambalapitiya 디렉터리 바인딩? sh파일을 바인딩 하시겠습니까? 당신이 무슨 뜻인지 확실하지. 디렉토리의 모든 내용을 포함하려면 go-bindata. 예를 들어 //go:generate $GOPATH/bin/go-bindata -prefix=data/ -pkg=$GOPACKAGE data/(생성되지 않은) go 파일을 go generate ./...넣으면 패키지의 디렉토리에서 go-bindata를 실행하여 데이터 하위 디렉토리에 모든 것을 포함하지만 'data /'접두사는 제거합니다.
Mark

1

go-bindata다른 답변에서 언급 한 인기있는 대안으로 mjibson / esc 는 임의의 파일도 포함하지만 특히 편리하게 디렉토리 트리를 처리합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.