I am trying to upload a thumbnail for youtube video in this way:
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
func main() {
url := "https://www.googleapis.com/youtube/v3/thumbnails/set?videoId=kU7okI-_vvU&key=[API_KEY]Type=media"
imageRef, err := os.Open("test.png")
if err != nil {
log.Fatal("os.Open", err)
}
rd := bufio.NewReader(imageRef)
req, err := http.NewRequest("POST", url, rd)
if err != nil {
log.Fatal("http.NewRequest", err)
}
log.Println(req.Body)
req.Header.Add("authorization", "Bearer [ACCESS_TOKEN]")
req.Header.Add("content-type", "image/png")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal("http.DefaultClient", err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal("ioutil.ReadAll", err)
}
fmt.Println(res)
fmt.Println(string(body))
}
I am getting this response:
"error": {
"code": 400,
"message": "The request does not include the image content.",
"errors": [
{
"message": "The request does not include the image content.",
"domain": "youtube.thumbnail",
"reason": "mediaBodyRequired",
"location": "body",
"locationType": "other"
}
]
}
}
I am including the image body in the POST request. Yet the respose says "The request does not include the image content.". Can anyone please help with this.
API requires that max file size be 2MB and I have ensured this.
Thank you.
PS: Although not shown in the code, result is tested with error handling.
Using bare HTTP methods for invoking YouTube Data API can be quite tricky at times.
You should have been employing the Google API Client Library for Go instead. See also the official Go Quickstart.
In case you stick with your current code, you need to have the following:
the invoking URL should contain the parameter uploadType=media, and
the Content-Type header should be passed on to the HTTP call with a value of kind image/png (as already suggested above).
the Content-Length header should also be set by your code (since that's not done within the NewRequestWithContext function; see also this function's doc, as #Peter indicated below).
Also you have to change your URL, since, according to the official doc, the API endpoint's URL is:
https://www.googleapis.com/upload/youtube/v3/thumbnails/set.
Do notice that your URL is missing the /upload/ path component.
Related
I am writing a code in GoLang to get data from a url but when I make http.Get() request, the response I get is as per the following.
"compile_output": "# command-line-arguments\n/usr/local/go-1.13.5/pkg/tool/linux_amd64/link: resize output file failed: truncate $WORK/b001/exe/a.out: file too large\n",
Please suggest me how to tackle this.
Code is as per the following:
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// define the URL to get data from
url := "my_example_url"
// use http.Get() to make a GET request to the URL
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error getting data:", err)
return
}
defer resp.Body.Close()
// read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
// print the response body
fmt.Println(string(body))
}
I tried reading through GoLang URl Documentations, but didn't help.
We are working with Prometheus HTTP API, and we're sending GET requests to the following endpoint:
/api/v1/query
At the beginning things worked as expected, but recently when our queries got bigger, request-URI became too large.
Docs says that it is possible to send POST request to the same endpoint, and pass the query parameter directly in the request body, instead of passing a query param as part of the URL..
This should solve our problem, but I couldn't find any example or guidelines explaining how to do it.
URL query length is limited, so we are looking for a way to send the query as part of the body :
End-point : http://server:8082/api/v1/query
Body :
{
"query": "count(count(node_cpu_seconds_total{instance=~\"iServer.*\",job=\"events_prometheus\"}) by (cpu))"
}
Response Error :
{
"status": "error",
"errorType": "bad_data",
"error": "invalid parameter 'query': parse error at char 1: no expression found in input"
}
Just to mention that sending the same query, as a query param, will work and give us the expected results.
You can URL-encode these parameters directly in the request body by using the POST method and Content-Type: application/x-www-form-urlencoded header. This is useful when specifying a large query that may breach server-side URL character limits.
If you use some programming language, you should build these requests like in the example below.
Example code in golang:
func main() {
formData := url.Values{
"query": {"sum(temperature) by(status)"},
"time": {"1670859244"},
}
client := &http.Client{}
req, err := http.NewRequest("POST", "http://localhost:8428/api/v1/query", strings.NewReader(formData.Encode()))
if err != nil {
log.Fatalln(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
log.Println(string(body))
}
In the response, you should see something like this.
{"status":"success","data":{"resultType":"vector","result":[]}}
But in the result, there should be some vector with data.
An example of the response can check here
https://docs.victoriametrics.com/keyConcepts.html#range-query
I'm attempting to use Go to write a utility that authenticates and uploads a file by making a multipart http request to our server. Everything seems to go okay, except the file is not arriving on the server. Looking at it further it appears the multipart in the request is empty. Code and request output below. What am I missing in my Go code?
The Code: (I've changed the URL...)
package main
import (
"net/http"
"mime/multipart"
"strings"
"fmt"
"io/ioutil"
"io"
"os"
"bytes"
"flag"
"encoding/json"
)
var (
filename = flag.String("filename", "", "file to upload")
name = flag.String("name", "", "name to give file on server")
username = flag.String("username", "", "username for authentication")
password = flag.String("password", "", "password for authentication")
)
func main() {
flag.Parse()
// Create multipart
var b bytes.Buffer
w := multipart.NewWriter(&b)
f, _ := os.Open(*filename) //open file to send
defer f.Close()
fw, err := w.CreateFormFile("file", *name) //give file a name
if err != nil {
fmt.Println(err)
}
if _, err := io.Copy(fw, f); err != nil { //copy the file to the multipart buffer
fmt.Println(err)
}
w.Close()
// print the head of the multipart data
bs := b.Bytes()
fmt.Printf("%+v\n\n", string(bs[:1000]))
// Send authentication/login
r, e := http.Post("https://mysite/login", "application/json", strings.NewReader(fmt.Sprintf("{\"username\":\"%s\",\"password\":\"%s\"}", *username, *password)))
if e != nil {
fmt.Println(e)
} else {
// Get the token from the body
type Body struct {
Token string
}
// convert json to get the token
body, _ := ioutil.ReadAll(r.Body)
bd := bytes.NewBuffer(body)
dec := json.NewDecoder(bd)
var m Body
dec.Decode(&m)
// Upload file
req, err := http.NewRequest("POST", "https://mysite/api/apps", &b)
if err != nil {
fmt.Printf("%v\n", err)
}
req.Header.Set("Authentication", fmt.Sprintf("Bearer: %s", m.Token))
req.Header.Set("Content-Type", w.FormDataContentType())
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
fmt.Printf("%v\n", err)
}
// print status and request body
fmt.Println(res.Status)
fmt.Printf("%+v\n", res.Request)
}
}
The first thing I print is the byte buffer, b, which contains the multipart data, everything looks good from here. (it was an xml file)
--83451b003d8e5cc38c0e8f60ad318e522cad4818cf293745c84ec36d26d5
Content-Disposition: form-data; name="file"; filename="snapshot-162224-820-99"
Content-Type: application/octet-stream
<manifest>
<projects>
<project name=........
Next I print the status of the request.
200 OK
Then I printed out the request structure, here is where I saw the MultipartForm was empty.
&{Method:GET URL:https://mysite/home/ Proto: ProtoMajor:0 ProtoMinor:0 Header:map[Authentication:[Bearer: DY0LCJL0g] Content-Type:[multipart/form-data; boundary=83451b003d8e5cc38c0e8f60ad318e522cad4818cf293745c84ec36d26d5] Referer:[http://mysite/home/]] Body:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host: Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:0xc42018a360 ctx:<nil>}
I highly doubt if the server really received nothing. The behavior of the printed body having a nil body is expected and documented in http.Response
// Request is the request that was sent to obtain this Response.
// Request's Body is nil (having already been consumed).
// This is only populated for Client requests.
Request *Request
If you want to debug the request body sented, you should either use a mock server or a proxy.
On another note, your code's attempt to login is not going to work. It does not maintain the cookie of login info, so the later requests cannot utilize them.
I wrote a simple web sever that gets the content of a given URL and writes it out using a http.ResponseWriter. But the problem is that it does not display any images and CSS on the page. The code is given bellow:
func factHandler(w http.ResponseWriter, r *http.Request) {
res, err := http.Get("http://www.meaningfultype.com/")
if err != nil {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
w.Write(robots)
}
What do I need to change so that the the whole page content as seen in the web browser is returned?
The problem here is presumably that the website you refer is using relative paths for the images and stylesheets, e.g. "/css/main.css". The local website that you deliver to the browser with your Go service has another domain (for example localhost) and thus the relative path cannot be resolved by the browser (there is no http://localhost/css/main.css).
So what you need to do is either set a base URL in the document you deliver or, alternatively, rewrite every relative path to an absolute path (/css/main.css to http://originalwebsite.com/css/main.css).
For adding the base tag or rewriting the URLs in the document I suggest employing something like goquery which lets you manipulate the HTML structure similar to how jQuery does it.
Example of adding a <base> tag:
import (
"github.com/PuerkitoBio/goquery"
"fmt"
)
func main() {
doc,err := goquery.NewDocument("http://www.meaningfultype.com/")
if err != nil {
panic(err)
}
m := doc.Find("head")
m.Children().First().BeforeHtml(`<base url="http://localhost/">`)
fmt.Println(doc.Html())
}
TL;DR
You are only serving the root HTML page, you need to respond to requests to other resources too (you can see what resource is being requested with the URL variable of the *http.Request variable)
When serving the resources, you need to write the Header's Content-Type to let the browser know what's the type of the resource (like image/png)
Full Answer
What you are doing in your request handler is getting http://www.meaningfultype.com/, the HTML page, then the browser finds an image like /images/header-logo.png and makes the request to get it but your server in localhost doesn't know how to respond to http://localhost/images/header-logo.png.
Assuming your handler function is handling requests at the root of the server ("/"), you could get the requested URL r.URL and use it to get the required resource:
url := "http://www.meaningfultype.com/"
if r.URL.Host == "" {
url += r.URL.String()
} else {
url = r.URL.String()
}
res, err := http.Get(url)
The problem is that, even after doing this, all the resources are sent as plain/text because you are not setting the Header's Content-Type. That's why you need to specify the type of the resource before writing. In order to know what the type of the resource is, just get it from the Content-Type you just received in the Header of the response of http.Get:
contentType := res.Header.Get("Content-Type")
w.Header().Set("Content-Type", contentType)
w.Write(robots)
The final result:
package main
import (
"io/ioutil"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", factHandler)
http.ListenAndServe(":8080", nil)
}
func factHandler(w http.ResponseWriter, r *http.Request) {
url := "http://www.meaningfultype.com/"
if r.URL.Host == "" {
url += r.URL.String()
} else {
url = r.URL.String()
}
res, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
contentType := res.Header.Get("Content-Type")
w.Header().Set("Content-Type", contentType)
w.Write(robots)
}
My account is connected to a CMS but I can't see Youtube Content ID in API Library. However, I see it in my Enabled APIs!! (It appeared after I try "Authorize requests using OAuth 2.0" in Youtube Content ID API reference doc). I can test the API in reference doc and it shows data from my CMS. But when I call the API from my program, the response is always like this:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "Not Found"
}
],
"code": 404,
"message": "Not Found"
}
}
This is my implementation using Go:
func TestYoutubeAPI(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadFile("./google-service-key.json")
if err != nil {
log.Fatal(err)
}
config, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/youtubepartner", "https://www.googleapis.com/auth/youtube.force-ssl", "https://www.googleapis.com/auth/youtube")
if err != nil {
log.Fatal(err)
}
client := config.Client(oauth2.NoContext)
request, _ := http.NewRequest("GET", "https://www.googleapis.com/youtube/partner/v1/assetSearch", nil)
request.Header.Add("Accept", "application/json")
query := request.URL.Query()
query.Add("createdAfter","2015-01-29T23:00:00Z")
query.Add("key", "XXX")
request.URL.RawQuery = query.Encode()
response, err := client.Do(request)
if err != nil {
fmt.Fprintln(w, err)
} else {
responseBody, _ := ioutil.ReadAll(response.Body)
fmt.Println(response.Status)
fmt.Fprintln(w, string(responseBody))
}
}
Other APIs (Youtube Data API) work fine with this code. However, I can't get my expected result with Youtube Content ID API.
Anyone got experience in this please help me. Thank you.
I found the answer!
Because I used Google Service Account to interact with Google API. It seems to create a new user account. So that I have to grant permission on Youtube CMS by adding Service account client email to Youtube CMS. Finally, I can access Youtube CMS using the API.