Utilize AJAX to upload files to Blobstore in Google App Engine

I'm currently developing a project using Go for Google App Engine. I've encountered an issue with uploading files/images to blobstore without requiring a site reload.

Here is the HTML form:

<form id="file-form" action="{{.UploadUrl}}" method="POST" enctype="multipart/form-data">
    <fieldset style="width: 100%;" data-uk-margin>
        <div class="uk-form-row">
            <div id="fields"></div>
        </div>

        <div class="uk-form-row" style="padding-top: 5px; padding-bottom: 5px;">
            <textarea id="textArea" cols="" rows="10" name="description" placeholder="Description"></textarea>
        </div>                      
        <div class="uk-form-row" style="padding-top: 5px; padding-bottom: 5px;">
            <p>Upload File: </p> <input id="file-select" type="file" name="file-select" accept="image/png">
        </div>                              
        <div class="uk-form-row" style="opacity: 1.0;">
            <div class="uk-flex uk-flex-center" id="buttonDiv">
               <button type="submit" id="submitButton" class="uk-button uk-button-primary">Upload</button>
            </div>
        </div>
    </fieldset>
</form>

And here is the JavaScript code to handle image upload:

var form = document.getElementById("file-form");
            var fileSelect = document.getElementById("file-select");
            var uploadButton = document.getElementById("submitButton");
            var description = document.getElementById("textArea");

            form.onsubmit = function(event) {
                event.preventDefault();

                uploadButton.innerHTML = "Uploading...";

                if (fileSelect.files.length == 0) {
                    alert("No images selected");
                    uploadButton.innerHTML = "Upload";
                    return;
                }

                var file = fileSelect.files[0];
                console.log(file.name);
                var formData = new FormData();
                formData.append("file", file);
                formData.append("description", description.value);

                var xhr = new XMLHttpRequest();
                xhr.open('POST', "/api/files/fileUpload", true);

                xhr.onload = function() {
                    if (xhr.status === 200) {
                        uploadButton.innerHTML = "Upload"
                    } else {
                        alert("An error occurred!");
                    }
                }
                xhr.send(formData);
            }

Go method to handle file upload:

c := appengine.NewContext(r)
    u := user.Current(c)

    if u == nil {
        url, err := user.LoginURL(c, r.URL.String())
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Set("Location", url)
        w.WriteHeader(http.StatusFound)
        return
    }

    logoutUrl, e := user.LogoutURL(c, "/redirect")
    if e != nil {
        panic(e)
    }
    email := u.Email

    uploadURL, error := blobstore.UploadURL(c, "/api/files/fileUpload", nil)
    if error != nil {
        panic(error)
    }

    data := WebpageData{LogoutUrl: logoutUrl, UserName: email, UploadUrl: uploadURL}

    template := template.Must(template.New("template").Parse(fileValue("./console/page/newForm.html"))

    err := template.Execute(w, data)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }

Lastly, let's address the errors that are causing trouble: 1. From the JavaScript console in the browser:

Failed to load resource: the server responded with a status of 500 (Internal Server Error)
  1. From the Go console/log:

    2015/04/13 15:10:15 http: panic serving 127.0.0.1:49543: mime: no media type goroutine 16 [running]: net/http.func·011() /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/net/http/server.go:1130 +0xbb api.check(0xb0db00, 0xc20800a970) api/api.go:40 +0x50 api.fileUploadHandler(0xb136e0, 0xc208045540, 0xc2080f1d40) api/files.go:19 +0x8a net/http.HandlerFunc.ServeHTTP(0x5a2088, 0xb136e0, 0xc208045540, 0xc2080f1d40) /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/net/http/server.go:1265 +0x41 net/http.(*ServeMux).ServeHTTP(0xc20803a690, 0xb136e0, 0xc208045540, 0xc2080f1d40) /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/net/http/server.go:1541 +0x17d appengine_internal.handleFilteredHTTP(0xb136e0, 0xc208045540, 0xc2080f1d40) /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/appengine_internal/api_dev.go:98 +0x413 net/http.HandlerFunc.ServeHTTP(0x5a20f8, 0xb136e0, 0xc208045540, 0xc2080f1d40) /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/net/http/server.go:1265 +0x41 net/http.serverHandler.ServeHTTP(0xc208042120, 0xb136e0, 0xc208045540, 0xc2080f1d40) /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/net/http/server.go:1703 +0x19a net/http.(*conn).serve(0xc2080454a0) /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/net/http/server.go:1204 +0xb57 created by net/http.(*Server).Serve /private/var/folders/00/0v42r000h01000cxqpysvccm003chb/T/appengine/go_appengine/goroot/src/net/http/server.go:1751 +0x35e

If you have any insights on how to resolve this issue, please share. Thank you for your help :)

Answer №1

Deprecated Blobstore functionality can be replaced with uploading files to Google Cloud Storage (GCS). Here is a sample code snippet demonstrating how to upload files to GCS:

import (
    // necessary imports for App Engine
    "golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
    "google.golang.org/appengine"
    "google.golang.org/appengine/datastore"
    "google.golang.org/appengine/file"
    "google.golang.org/appengine/urlfetch"
    "google.golang.org/cloud"
    "google.golang.org/cloud/storage"
)

func serveFilesUpload(res http.ResponseWriter, req *http.Request) {
    ctx := appengine.NewContext(req)

    bucket, err := file.DefaultBucketName(ctx)
    if err != nil {
        panic(err)
    }

    hc := &http.Client{
        Transport: &oauth2.Transport{
            Source: google.AppEngineTokenSource(ctx, storage.ScopeFullControl),
            Base:   &urlfetch.Transport{Context: ctx},
        },
    }

    id := uuid.NewRandom().String()

    ff, _, err := req.FormFile("file")
    if err != nil {
        panic(err)
    }
    defer ff.Close()

    cctx := cloud.NewContext(appengine.AppID(ctx), hc)
    wc := storage.NewWriter(cctx, bucket, id)
    io.Copy(wc, ff)
    
    err = wc.Close()
    if err != nil {
        panic(err)
    }

    json.NewEncoder(res).Encode(id)
}

You can find a complete example in this GitHub repository.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to download a dynamically generated PHP file to your local machine

I am trying to find a solution where the search results can be downloaded by the user and saved on their computer. Currently, the file is automatically stored on the server without giving the user an option to choose where to save it. In the search form, ...

The double click functionality does not seem to be functioning within the Backbone View

I am trying to detect double click event within a Backbone view var HomePage = Backbone.View.extend({ initialize: function(){ this.render(); }, render: function(){ var template = _.template($('#app1').html()); ...

Difficulty invoking jQuery ajax in conjunction with JavaScript confirmation modal

Upon clicking Delete this post, a modal window appears to confirm the user's intention. If the user proceeds by clicking OK, the AJAX process continues as expected. However, if Cancel is selected, the deletion action still occurs. I have attempted u ...

Creating a compact Swiper slider: reducing image size without sacrificing full window coverage!

Utilizing Swiper to craft a slider that occupies the entire window, with a slight margin for a bar-- no issues there. (https://i.sstatic.net/qcGBA.jpg) However, the image I placed in for the slide () appears to be excessively enlarged. Is there a way to ...

Best practice for triggering events based on mysql datetime with the help of ajax

Currently engaged in a CRM project aimed at creating a user-friendly backoffice system where clients can be added and events such as follow-ups and SLA controls can be defined. The goal is to trigger notifications on the webpage only for new events. For e ...

Guidelines on concealing the parent component while the child component is loading in Angular 2

In my latest project, the view setup is as follows: https://i.sstatic.net/VxJ9U.png Upon page load, the Parent Item Description should be visible while the Selected sub item description remains hidden. When a Sub Item x is selected, the Parent Item Desc ...

When attempting to check and uncheck checkboxes with a specific class, the process fails after the first uncheck

I have a set of checkboxes and one is designated as "all." When this box is clicked, I want to automatically select all the other checkboxes in the same group. If the "all" box is clicked again, I would like to deselect all the other checkboxes. Currently ...

Error: The property 'setCrossOrigin' is not defined and cannot be read

When I try to run both .obj and .mtl files together, I encounter an error, whereas running just the .obj loader works fine. The specific error message that appears is: MTLLoader error Below is the code snippet I am using to load the .obj and .mtl files: ...

Enhance Vaadin 14: Automatically adjust TextArea size when window is resized

Using Vaadin 14.1.19 in a project called "My Starter Project," I attempted to create a TextArea that supports multiple lines. Initially, everything seemed fine, but upon resizing the TextArea, it failed to adjust the number of visible lines. Here is the co ...

What is causing this array to automatically alter its value?

How can I prevent A from changing its value when I only want to modify Q in the first loop of this html/javascript code? Why does A change even though I am only changing Q? What steps can I take to avoid A from changing? <!DOCTYPE html> <html& ...

Customize and adjust the default color for dark themes in Material-UI

When using Material-UI and switching to a dark theme, you may notice that some components change their color to #424242 while others change to #212121. This color inconsistency stems from the use of theme.palette.grey: theme.palette.grey[800]: '#424 ...

What is causing my Django pagination to continuously display the same items over and over again without change?

I am currently working on implementing an infinite scroll feature using Django and AJAX on my website. The goal is to dynamically load more items from the database when a user reaches the bottom of the page. While everything seems to be functioning correct ...

Three.js - Optimizing and Handling Images - Best Practices

My current project has a unique concept: https://i.sstatic.net/Jd3Ot.jpg The main idea revolves around a virtual room that dynamically adjusts its height based on the number of images loaded by the user. While it functions smoothly with fewer pictures, ...

Issue with Guild Member Add functionality in discord.js is not functioning as expected

I'm facing an issue with my code where the bot does not send a welcome message when a user joins, despite having everything set up correctly. Even when a user leaves, there is no farewell message being sent either. Here is the code I am using: bot.on ...

What is the best way to extract all query parameters in React-Router-DOM v6 using `useSearchParams` without having to specify the key?

I'm currently developing a React project using react-router-dom v6 and my goal is to extract all query parameters. http://localhost:3000/users?page=5&pageSize=25 The specific query parameters I am looking for are page and pageSize. I am aware of ...

Encountering a three.js issue while generating a large number of point lights

//////twinkling stars for (let index = 0; index < 1000; index++) { const stargeometry = new THREE.SphereGeometry(1, 24, 24); //create star sphere size const starmaterial = new THREE.MeshStandardMaterial({ color: 0xffffff }); //define star textur ...

Unable to convert data into action parameters for jQuery Ajax in ASP.Net MVC

In my MVC Action, I have the following code: [HttpPost] public virtual ActionResult AddAttachment(Guid scheduleId, DropZoneResultViewModel file2) { // do something here return Json(true); } Additionally, I am using this Java ...

Issues arise when attempting to use the Android KeyUp, KeyDown, and KeyPress events in conjunction with Angular2

I am encountering an issue where I consistently receive a keyCode of 229 in Android Chrome browsers when running either: <input type="text" (keydown)="testKeyCodes($event)"/> <!-- or --> <input type="text" (keyup)="testKeyCodes($event)"/& ...

Error encountered: AXIOS request failed due to XSRF-TOKEN Mismatch

My server is receiving different X-XSRF-TOKEN headers compared to the cookies being sent when I make requests 2-3 times per second using axios. let axiosInstance = axios.create({ baseUrl: "MY_BASE_URL_HERE", timeout: 20000 } ...

Error: Invalid Request - Nodejs Request Method

As part of my project involving payment gateway integration, I needed to make a call to the orders api. However, I encountered an error message: {"error":{"code":"BAD_REQUEST_ERROR","description":"Please provide your api key for authentication purposes."} ...