ipa and apk
Version
Interface
import (
"errors"
)
// GenerateEXT ...
func GenerateEXT(ext string) (Ires, error) {
switch ext {
case "ipa":
return &ios{}, nil
case "apk":
return &android{}, nil
default:
return nil, errors.New("副檔名必須是ipa或apk")
}
}
// GenerateENV ...
func GenerateENV(env string) (Ires, error) {
switch env {
case "ios":
return &ios{}, nil
case "android":
return &android{}, nil
default:
return nil, errors.New("下載時取得環境錯誤")
}
}
// Ires ....
type Ires interface {
Version(string) (string, error)
Env() string
Rename() error
ReadFile() ([]byte, error)
CompareVersion(string) int
}
func parseVersion(s string, width int) int64 { //為版本加上填充字元以便比較兩版本大小
strList := strings.Split(s, ".")
format := fmt.Sprintf("%%s%%0%ds", width)
v := ""
for _, value := range strList {
v = fmt.Sprintf(format, v, value)
}
var result int64
var err error
if result, err = strconv.ParseInt(v, 10, 64); err != nil {
fmt.Printf("ugh: parseVersion(%s): error=%s", s, err)
return 0
}
// fmt.Printf("parseVersion: [%s] => [%s] => [%d]\n", s, v, result)
return result
}
Android
import (
"auth/src/globals"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/shogo82148/androidbinary/apk"
)
type android struct {
}
func (a *android) Rename() (err error) {
err = os.Remove(fmt.Sprintf("%s/android/app.apk", globals.Config.Setting.Uploads))
err = os.Rename(fmt.Sprintf("%s/android/temp.apk", globals.Config.Setting.Uploads), fmt.Sprintf("%s/android/app.apk", globals.Config.Setting.Uploads))
return
}
func (a *android) Env() string {
return "android"
}
func (a *android) Version(filename string) (version string, err error) {
path := filepath.Join(globals.Config.Setting.Uploads, "android", filename+".apk")
pkg, errpkg := apk.OpenFile(path)
if errpkg != nil {
err = errpkg
return
}
defer pkg.Close()
return pkg.Manifest().VersionName.String()
}
func (a *android) ReadFile() (file []byte, err error) {
return ioutil.ReadFile(fmt.Sprintf("%s/android/app.apk", globals.Config.Setting.Uploads))
}
func (a *android) CompareVersion(version string) (check int) {
newVersion := parseVersion(version, 4)
var oldVersion = parseVersion(globals.ApkVersion, 4)
switch {
case newVersion > oldVersion:
globals.ApkVersion = version
check = 1
case newVersion < oldVersion:
check = -1
default:
check = 0
}
return
}
IOS
import (
"archive/zip"
"auth/src/globals"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"howett.net/plist"
)
type ios struct {
bid string // CFBundleIdentifier
bver string // CFBundleVersion
btitle string // CFBundleName
}
type buildPlist struct {
Items []item `plist:"items"`
}
type item struct {
Assets []asset `plist:"assets"`
Metadata meta `plist:"metadata"`
}
type meta struct {
BundleIdentifier string `plist:"bundle-identifier"`
BundleVersion string `plist:"bundle-version"`
Kind string `plist:"kind"`
Title string `plist:"title"`
}
type asset struct {
Kind string `plist:"kind"`
URL string `plist:"url"`
}
func (i *ios) Rename() (err error) {
err = os.Remove(fmt.Sprintf("%s/ios/app.ipa", globals.Config.Setting.Uploads))
err = os.Rename(fmt.Sprintf("%s/ios/temp.ipa", globals.Config.Setting.Uploads), fmt.Sprintf("%s/ios/app.ipa", globals.Config.Setting.Uploads))
data := &buildPlist{
Items: []item{
{
Assets: []asset{
{
Kind: "software-package",
URL: fmt.Sprintf("%s/ios/app.ipa", globals.Config.IOSDL[globals.Env].URL),
},
},
Metadata: meta{
BundleIdentifier: i.bid,
BundleVersion: i.bver,
Kind: "software",
Title: i.btitle,
},
},
},
}
buf := &bytes.Buffer{}
encoder := plist.NewEncoder(buf)
if err = encoder.Encode(data); err != nil {
fmt.Println(err)
}
f, err := os.Create(fmt.Sprintf("%s/ios/app.plist", globals.Config.Setting.Uploads))
if err != nil {
return err
}
defer f.Close()
buffer := make([]byte, 1024)
for {
// read a chunk
n, err := buf.Read(buffer)
if err != nil && err != io.EOF {
return err
}
if n == 0 {
break
}
// write a chunk
if _, err := f.Write(buffer[:n]); err != nil {
return err
}
}
return
}
func (i *ios) Env() string {
return "ios"
}
func (i *ios) Version(filename string) (version string, err error) {
path := filepath.Join(globals.Config.Setting.Uploads, "ios", filename+".ipa")
zr, errzip := zip.OpenReader(path)
if errzip != nil {
err = errzip
return
}
defer zr.Close()
for _, file := range zr.File { // 讀取plist檔
if strings.Contains(file.Name, "Info.plist") {
nameSplit := strings.Split(file.Name, "/")
if len(nameSplit) == 3 && nameSplit[2] == "Info.plist" {
fp, errfile := file.Open()
if errfile != nil {
err = errfile
return
}
bs, _ := ioutil.ReadAll(fp)
fp.Close()
var result map[string]interface{}
_, errplist := plist.Unmarshal(bs, &result)
if errplist != nil {
err = errplist
return
}
// for k, v := range result {
// log.Println(fmt.Sprintf("%s: %v", k, v))
// }
shortVersion, okSV := result["CFBundleShortVersionString"]
if okSV {
// fmt.Printf("CFBundleShortVersionString: %s", shortVersion.(string))
version = shortVersion.(string)
}
bid, okID := result["CFBundleIdentifier"]
if okID {
i.bid = bid.(string)
// fmt.Printf("CFBundleIdentifier: %s", i.bid)
}
bver, okVer := result["CFBundleVersion"]
if okVer {
i.bver = bver.(string)
// fmt.Printf("CFBundleVersion: %s", i.bver)
}
btitle, okTitle := result["CFBundleName"]
if okTitle {
i.btitle = btitle.(string)
// fmt.Printf("CFBundleName: %s", i.btitle)
}
return
}
}
}
return
}
func (i *ios) ReadFile() (file []byte, err error) {
return ioutil.ReadFile(fmt.Sprintf("%s/ios/app.plist", globals.Config.Setting.Uploads))
}
func (i *ios) CompareVersion(version string) (check int) {
newVersion := parseVersion(version, 4)
var oldVersion = parseVersion(globals.IpaVersion, 4)
switch {
case newVersion > oldVersion:
globals.IpaVersion = version
check = 1
case newVersion < oldVersion:
check = -1
default:
check = 0
}
return
}
Download
// Download ...
func Download(ctx iris.Context) {
env := ctx.FormValue("env")
res, err := resource.GenerateENV(env)
if err != nil {
ctx.StatusCode(400)
return
}
if env == "ios" { //IOS需要轉址
redir := fmt.Sprintf("itms-services://?action=download-manifest&url=%s/ios/app.plist", globals.Config.IOSDL[globals.Env].URL)
ctx.Redirect(redir)
return
}
ctx.Header("Content-Type", "application/vnd.android.package-archive")
ctx.Header("Access-Control-Allow-Origin", "*")
file, err := res.ReadFile()
if err != nil {
ctx.StatusCode(500)
return
}
ctx.Write(file)
}