【问题标题】:Download zipped file in browser from golang server从 golang 服务器在浏览器中下载压缩文件
【发布时间】:2015-03-13 12:22:05
【问题描述】:

我正在尝试从 Go 网络服务器下载压缩文件。我已经成功地压缩了文件,并且可以从我的服务器目录中解压缩它。我遇到的问题是提供文件并使用 Javascript 下载它。

这是我的代码的概述:

1) 向从另一个端点检索数据的服务器发出请求

2) 根据用户想要的文件类型(CSV(setupCSVRows function) or JSON)构造返回的数据

3) 将字节从缓冲区写入文件并返回文件地址

4) 当用户点击一个链接时,使用文件地址发出http get请求,并在新窗口中打开内容进行下载

每次我尝试解压缩文件时都会收到错误消息:存档文件不完整(使用 Unarchiver 程序)并且 Mac 上的默认存档实用程序会显示一个简短的加载屏幕,然后关闭。

转码:

func ExportData(writer http.ResponseWriter, req *http.Request, session sessions.Session) (int, string) {

headers := HeaderCreation{
    OriginalRequest: req,
    Session:         session,
}

qs := req.URL.Query()

if len(qs["collectionID"]) != 1 {
    return 400, "ERROR: Must submit one collectionID in query string"
}
if len(qs["fileType"]) != 1 {
    return 400, "ERROR: Must submit one fileType in query string"
}

collID := qs["collectionID"][0]
fileType := qs["fileType"][0]

url := "http://" + config.Data.Address + "/api/" + collID
response, err := httpClient.DoSystemRequest("GET", url, nil, headers)

if err != nil {
    return 500, "ERROR: Could not resolve DataURL/api" + err.Error()
} else {
    contents, err := ioutil.ReadAll(response.Body)
    response.Body.Close()

    if err != nil {
        return 400, "ERROR: Response from Platform unreadable"
    }

    buf := new(bytes.Buffer)

    w := zip.NewWriter(buf)

    file, err := w.Create(collID + "." + fileType)
    if err != nil {
        return 400, "ERROR: Unable to create zip file with name of: " + collID + " and type of: " + fileType + "; " + err.Error()
    }

    switch fileType {
    case "csv":

        rows, err := setupCSVRows(contents)

        if err != nil {
            return 400, err.Error()
        }

        _, err = file.Write(rows)
        if err != nil {
            return 400, "Unable to write CSV to zip file; " + err.Error()
        }
    case "json":
        _, err := file.Write(contents)
        if err != nil {
            return 400, err.Error()
        }
    } // end switch

    err = w.Close()
    if err != nil {
        return 400, "ERROR: Unable to close zip file writer; " + err.Error()
    }

    //create fileName based on collectionID and current time
    fileAddress := collID + strconv.FormatInt(time.Now().Unix(), 10)

    //write the zipped file to the disk
    ioutil.WriteFile(fileAddress + ".zip", buf.Bytes(), 0777)

    return 200, fileAddress
} //end else
}

func ReturnFile(writer http.ResponseWriter, req *http.Request) {
queries := req.URL.Query()
fullFileName := queries["fullFileName"][0]
http.ServeFile(writer, req, fullFileName)
//delete file from server once it has been served
//defer os.Remove(fullFileName)
}

func setupCSVRows(contents []byte) ([]byte, error) {
//unmarshal into interface because we don't know json structure in advance
var collArr interface{}
jsonErr := json.Unmarshal(contents, &collArr)

if jsonErr != nil {
    return nil, errors.New("ERROR: Unable to parse JSON")
}

//had to do some weird stuff here, not sure if it's the best method
s := reflect.ValueOf(collArr)
var rows bytes.Buffer
var headers []string

for i := 0; i < s.Len(); i++ {
    var row []string
    m := s.Index(i).Interface()

    m2 := m.(map[string]interface{})

    for k, v := range m2 {

        if i == 0 {
            if k != "item_id" {
                headers = append(headers, k)
            }
        }
        if k != "item_id" {
            row = append(row, v.(string))
        }
    }

    if i == 0 {
        headersString := strings.Join(headers, ",")
        rows.WriteString(headersString + "\n")
    }
    rowsString := strings.Join(row, ",")
    rows.WriteString(rowsString + "\n")
}

return rows.Bytes(), nil
}

Javascript 代码:

$scope.exportCollection = function(fileType) {
    $scope.exporting = true;
    $scope.complete = false;

    $http.get('/api/batch/export?collectionID=' + $scope.currentCollection.collectionID + '&fileType=' + fileType.toLowerCase()).success(function(data){
    $scope.fileAddress = data;

    }).error(function(err) {
  console.log(err);
    });

};

$scope.downloadFile = function() {
    $http.get('/api/batch/export/files?fullFileName=' + $scope.fileAddress + ".zip")
      .success(function(data) {
        console.log(data);

    //window.open("data:application/zip;base64," + content);
    //var content = "data:text/plain;charset=x-user-defined," + data;
    var content = "data:application/zip;charset=utf-8," + data;
    //var content = "data:application/octet-stream;charset=utf-8" + data;
    //var content = "data:application/x-zip-compressed;base64," + data;
    //var content = "data:application/x-zip;charset=utf-8," + data;
    // var content = "data:application/x-zip-compressed;base64," + data;
    window.open(content);
  })
  .error(function(err) {
    console.log(err);
  })
}

如您所见,我尝试了许多不同的 URI 方案来下载文件,但没有任何效果。

是否需要在服务器端设置 MIME 类型?

任何帮助将不胜感激。如果我需要提供更多详细信息,请告诉我。

【问题讨论】:

  • 在 JS 中使用+ 操作符会强制转换成字符串吗?

标签: javascript angularjs go zip


【解决方案1】:

我无法评论(新用户)-但关于命名文件,只需在服务之前设置标题(使用了 ServeContent,但应该可以在这里互换):

 func serveFile(w http.ResponseWriter, r *http.Request){
    data, err := ioutil.ReadFile("path/to/file/and/file+ext")
    if(err != nil){
        log.Fatal(err)
    }
    w.Header().Set("Content-Type", "application/octet-stream")
    w.Header().Set("Content-Disposition", "attachment; filename=" + "fileName.here")
    w.Header().Set("Content-Transfer-Encoding", "binary")
    w.Header().Set("Expires", "0")
    http.ServeContent(w, r, "path/to/file/and/file+ext", time.Now(), bytes.NewReader(data))

}

【讨论】:

  • 我给 +1 以便您下次可以发表评论。
【解决方案2】:

我最终走上了一条稍微不同的路线。现在我在响应头上设置 MIME 类型并创建一个指向该文件的链接。

转码:

func ReturnFile(writer http.ResponseWriter, req *http.Request) {
queries := req.URL.Query()
fullFileName := queries["fullFileName"][0]

writer.Header().Set("Content-type", "application/zip")
http.ServeFile(writer, req, fullFileName)
//delete file from server once it has been served
defer os.Remove(fullFileName)
}

Angular 用户界面代码:

<a ng-show="complete" href="/api/batch/export/files?fullFileName={{fileAddress}}">Download {{currentCollection.name}}</a>

这会自动触发下载,并且 zip 文件不再损坏。

【讨论】:

  • 感谢您的示例!我的压缩文件下载为“下载”。有没有办法保留文件名?谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-01
  • 2012-02-21
  • 2016-09-06
相关资源
最近更新 更多