I'm trying to create a simple http service with the endpoint to download file to the local system in Go. The link comes in ?uri tag, but when I want to get it I receive an empty string. I tried to parse the form of my request but it didn't help. Here is my code:
func main() {
http.HandleFunc("/download", DownloadHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func DownloadHandler(writer http.ResponseWriter, request *http.Request) {
prsErr := request.ParseForm()
if prsErr != nil{
panic(prsErr)
}
uri := request.FormValue("?uri")
_, _ = writer.Write([]byte(uri))
err := DownloadFile("img.png", uri)
if err != nil {
panic(err)
}
}
func DownloadFile(filepath string, url string) error {
// Create the file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Write the body to file
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}
I will appreciate any help! Thank you!
invalid at request.FormValue("?uri")
uri := request.FormValue("uri")
Related
I am writing an API whichs has to redirect incoming requests to another service, the response must be forwarded to the original requester.
I figured a simple function like below should do the trick, but I was wrong.
I receive the data from my redirected response, however when I send it back to the initial request I receive this response without any data Could not get response. Error: socket hang up
If I try to execute the very same request using postman straight to the redirect URL it works perfectly fine.
func initialAssetsHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
return
}
resp, err := http.Post(conf.redirectURL, "application/json", bytes.NewReader(body))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
log.Info(string(buf.Bytes()))
var data json.RawMessage
if err = json.NewDecoder(resp.Body).Decode(&data); err != nil {
fmt.Println(err)
return
}
helper.SendJsonRaw(w, 200, data)
}
Here is the SendJsonRaw function:
func SendJsonRaw(w http.ResponseWriter, status int, r json.RawMessage) error {
w.Header().Set(HeaderContentType, MimeApplicationJSON)
w.WriteHeader(status)
_, err := w.Write(r)
return err
}
The r.Body is read by the json decoder up to EOF, then when you pass it to the redirect request it looks empty to the http.Client and therefore it sends no body. You need to retain the content of the body.
For example you can do the following:
func initialAssetsHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
return
}
var initialAssets TagAssets
if err := json.Unmarshal(&initialAssets, body); err != nil {
if !strings.Contains(err.Error(), "json: invalid use of ,string struct tag, trying to unmarshal") {
helper.SendJsonError(w, http.StatusBadRequest, err)
return
}
}
resp, err := http.Post(conf.redirectURL, "application/json", bytes.NewReader(body))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
log.Info(resp)
var data json.RawMessage
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
fmt.Println(err)
return
}
helper.SendJsonOk(w, data)
}
trying to post multipart/form-data image using go
image file receive from request client and already saved as multipart.File
here my code
func postImage(file multipart.File, url string, filename string) (*http.Response, error) {
r, w := io.Pipe()
defer w.Close()
m := multipart.NewWriter(w)
defer m.Close()
errchan := make(chan error)
defer close(errchan)
go func() {
part, err := m.CreateFormFile("file", filename)
log.Println(err)
if err != nil {
errchan <- err
return
}
if _, err := io.Copy(part, file); err != nil {
errchan <- err
return
}
}()
merr := <-errchan
if merr != nil {
return nil, merr
}
resp, err := http.Post(url, m.FormDataContentType(), r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return resp, err
}
when i try using it, it stuck at part, err := m.CreateFormFile("file", filename) never return anything
any solution?
Thanks
Use the pipe error to propagate the error back to the main goroutine. Close the write side of the pipe to prevent the client from blocking forever on read. Close the read side of the pipe to ensure that the goroutine exits.
func postImage(file multipart.File, url string, filename string) (*http.Response, error) {
r, w := io.Pipe()
// Close the read side of the pipe to ensure that
// the goroutine exits in the case where http.Post
// does not read all of the request body.
defer r.Close()
m := multipart.NewWriter(w)
go func() {
part, err := m.CreateFormFile("file", filename)
if err != nil {
// The error is returned from read on the pipe.
w.CloseWithError(err)
return
}
if _, err := io.Copy(part, file); err != nil {
// The error is returned from read on the pipe.
w.CloseWithError(err)
return
}
// The http.Post function reads the pipe until
// an error or EOF. Close to return an EOF to
// http.Post.
w.Close()
}()
resp, err := http.Post(url, m.FormDataContentType(), r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return resp, err
}
I have a long running app that I'd like to monitor in real time. HAR files allow me to do this after the fact, but as they are an "archive", they don't allow me to do this in real time.
Is their anyway to stream the "events" array of the HAR file so I can process them as they are generated?
This can be firefox or chrome.
So with some help from https://github.com/mafredri/cdp/tree/master/example/screencast I figured out how to do this in go with chrome's debugger api
What this code doesn't do is tie the request body to the response (where it isn't available), but as I show the RequestID will be consistent so if one serializes event processing (say via locking) one can save the body and use it when the response event is seen.
package main
import (
"context"
"log"
"github.com/mafredri/cdp"
"github.com/mafredri/cdp/cdpcmd"
"github.com/mafredri/cdp/devtool"
"github.com/mafredri/cdp/rpcc"
)
func main() {
if err := run(); err != nil {
panic(err)
}
}
func run() error {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
devt := devtool.New("http://localhost:9222")
page, err := devt.Get(ctx, devtool.Page)
if err != nil {
return err
}
conn, err := rpcc.DialContext(ctx, page.WebSocketDebuggerURL)
if err != nil {
return err
}
defer conn.Close()
c := cdp.NewClient(conn)
err = c.Page.Enable(ctx)
if err != nil {
return err
}
loadEventFired, err := c.Page.LoadEventFired(ctx)
if err != nil {
return err
}
_, err = c.Page.Navigate(ctx, cdpcmd.NewPageNavigateArgs("https://github.com/"))
if err != nil {
return err
}
_, err = loadEventFired.Recv()
if err != nil {
return err
}
loadEventFired.Close()
a := &cdpcmd.NetworkEnableArgs{}
a.SetMaxResourceBufferSize(32000)
a.SetMaxTotalBufferSize(96000)
err = c.Network.Enable(ctx, a)
responseEvents, err := c.Network.ResponseReceived(ctx)
requestEvents, err := c.Network.RequestWillBeSent(ctx)
go func() {
defer responseEvents.Close()
for {
ev, err := responseEvents.Recv()
if err != nil {
log.Printf("Failed to receive network event: %v", err)
return
}
log.Printf("requestid = %v, url = %v", ev.RequestID, ev.Response.URL)
}
}()
go func() {
defer requestEvents.Close()
for {
ev, err := requestEvents.Recv()
if err != nil {
log.Printf("Failed to receive network event: %v", err)
return
}
log.Printf("requestid = %v, url = %v", ev.RequestID, ev.Request.URL)
}
}()
select {}
return nil
}
I would like to use golang post request, upload pictures, but I do not want to pass filepath, just want to pass [] byte
The following article are not what I need because they are used os.Open
golang POST data using the Content-Type multipart/form-data
func Upload(url, file string) (err error) {
// Prepare a form that you will submit to that URL.
var b bytes.Buffer
w := multipart.NewWriter(&b)
// Add your image file
f, err := os.Open(file)
if err != nil {
return
}
defer f.Close()
fw, err := w.CreateFormFile("image", file)
if err != nil {
return
}
if _, err = io.Copy(fw, f); err != nil {
return
}
// Add the other fields
if fw, err = w.CreateFormField("key"); err != nil {
return
}
if _, err = fw.Write([]byte("KEY")); err != nil {
return
}
// Don't forget to close the multipart writer.
// If you don't close it, your request will be missing the terminating boundary.
w.Close()
// Now that you have a form, you can submit it to your handler.
req, err := http.NewRequest("POST", url, &b)
if err != nil {
return
}
// Don't forget to set the content type, this will contain the boundary.
req.Header.Set("Content-Type", w.FormDataContentType())
// Submit the request
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
return
}
// Check the response
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("bad status: %s", res.Status)
}
return
}
Since you use
if _, err = io.Copy(fw, f); err != nil {
return
}
You may as well edit your code to:
Add new import: "bytes"
Change the method signature to func Upload(url string, file []byte) (err error)
Use io.Copy(fw, bytes.NewReader(f))
How do I POST to an API with Content-Type: multipart/form-data, []byte parameters and string arguments? I have tried, but it is failing.
Error message:
details: "[301 301 Moved Permanently]<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<html>\r\n301 Moved Permanently\r\n<body bgcolor=\"white\">\r\n301 Moved Permanently\r\n<p>The requested resource has been assigned a new permanent URI.</p >\r\n<hr/>Powered by Tengine/2.1.0</body>\r\n</html>\r\n"
Go code:
func NewPost2(url string) ([]byte, error) {
m := make(map[string]interface{}, 0)
m["fileName"] ="good"
m["name"] = Base64ToByte("/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDHooor+wD+Zz//2Q==")
b, _ := json.Marshal(m)
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
httpReq.Header.Set("Content-Type", "multipart/form-data;charset=UTF-8")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
b, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
}
respData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return respData, nil
}
Now, I am very happy with the mood to share my solution
func NewPostFile(url string, paramTexts map[string]interface{}, paramFile FileItem) ([]byte, error) {
// if paramFiles ==nil {
// return NewPost(url,paramTexts,header,transport)
// }
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for k, v := range paramTexts {
bodyWriter.WriteField(k, v.(string))
}
fileWriter, err := bodyWriter.CreateFormFile(paramFile.Key, paramFile.FileName)
if err != nil {
fmt.Println(err)
//fmt.Println("Create form file error: ", error)
return nil, err
}
fileWriter.Write(paramFile.Content)
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
fmt.Println(bodyBuf.String())
resp, err := http.Post(url, contentType, bodyBuf)
if err != nil {
return nil, err
}
defer resp.Body.Close()
fmt.Println(resp)
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
b, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
}
respData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
fmt.Println(string(respData))
return respData, nil
}
type FileItem struct {
Key string //image_content
FileName string //test.jpg
Content []byte //[]byte
}
I wrapped the multipart code in a function, as you need to Close it before you
can make a request. Also my method is using # as a heuristic, similar to
cURL [1]:
package main
import (
"bytes"
"io"
"mime/multipart"
"os"
"strings"
)
func createForm(form map[string]string) (string, io.Reader, error) {
body := new(bytes.Buffer)
mp := multipart.NewWriter(body)
defer mp.Close()
for key, val := range form {
if strings.HasPrefix(val, "#") {
val = val[1:]
file, err := os.Open(val)
if err != nil { return "", nil, err }
defer file.Close()
part, err := mp.CreateFormFile(key, val)
if err != nil { return "", nil, err }
io.Copy(part, file)
} else {
mp.WriteField(key, val)
}
}
return mp.FormDataContentType(), body, nil
}
Example:
package main
import "net/http"
func main() {
form := map[string]string{"profile": "#portrait.jpg", "name": "John"}
ct, body, err := createForm(form)
if err != nil {
panic(err)
}
http.Post("https://stackoverflow.com", ct, body)
}
https://curl.se/docs/manpage.html#-F
On a 301 response, the new url is specified in the headers of the response, not its body
(see https://en.wikipedia.org/wiki/HTTP_301)
try printing :
resp.Header["Location"]
If you have this error as a final response, this also means that the http.Client chose to not follow this redirection.
The doc says that the dafult policy for a Client is to follow up to 10 redirects.
In order to debug redirects, you can write your own CheckRedirect function, which can for instance print the sequence of urls