Uploading a user profile image to s3 while registering

I’m building a social media site, and I got image uploading to s3 to work on the front end (like images on posts). What I cannot seem to get to work is uploading images on the user registration page. my front end is react and it’s easy to just do the aws upload code in there, but the registration page is a server side render page and the form has a csrf field of course. i figured out i can use request.multipart.file to upload the image to s3 from the server without storing it, and i figured out how to get around the multipart form breaking the csrf, but all of the validation of the other forms breaks regardless. even if i remove the validation, request.input(‘name’) is useless. I just cant get the other form data if i use processManually. here is my form code:

@section('content')
    <div class="default-form">
        <form action="/register?_csrf={{csrfToken}}" class="rform" method="POST" enctype="multipart/form-data" boundary='abvdefgh'>
            <div class="error-box
            @if(hasErrorFor('email') || hasErrorFor('password'))
                active
            @endif
            ">
                @if(hasErrorFor('email'))
                    {{ getErrorFor('email') }}
                @endif
                @if(hasErrorFor('password'))
                    {{ getErrorFor('password') }}
                @endif
                @if(hasErrorFor('database'))
                    {{ getErrorFor('database') }}
                @endif
            </div>
            <div class="form-group names">
                <label for="fname" class='fname'>
                    first
                    <input type="text" name="fname" value="{{ old('fname', '') }}">
                </label>
                <label for="lname" class="lname">
                    last
                    <input type="text" name="lname" value="{{ old('lname', '') }}">
                </label>
            </div>
            <div class="form-group">
                <label for="email">email</label>
                <input type="email" name="email" value="{{ old('email', '') }}" class="email
                @if(hasErrorFor('email'))
                    error
                @endif
                ">
            </div>

            <div class="form-group">
                <label for="profile_pic_upload">image file</label>
                <input type="file" name="profile_pic_upload" />
            </div>
            <div class="form-group">
                <label for="password">password</label>
                <input type="password" name="password" class="password
                @if(hasErrorFor('password'))
                    error
                @endif
                ">
            </div>
            <div class="form-group">
                <label for="confirm">[confirm]</label>
                <input type="password" name="confirm" class="confirm
                @if(hasErrorFor('confirm'))
                    error
                @endif
                ">
            </div>
            <div class="form-group">
                <button type='submit'>
                    <span></span>
                    <span></span>
                    <span></span>
                    <span></span>
                    [register]
                </button>
            </div>
            <hr>
                <a href="/login">~ have an account?</a>
        </form>
    </div>

@endsection

and here is the code for the authcontroller function:

async storeUser ({ request, session, response, auth }) {

        console.log(request)
        try {

            // console.log(request.input('profile_pic'))
            let uImg = '/img/user.jpg'
            request.multipart.file('profile_pic_upload', {}, async (file) => {
                await Drive.disk('s3').put(file.clientName, file.stream)

                if (await Drive.disk('s3').exists(file.clientName)) {

                    uImg = `https://${Env.get('S3_BUCKET')}.s3.amazonaws.com/${file.clientName}`
                }
            })

            await request.multipart.process()
            console.log(uImg)

            let first = 'somebody'
            if (request.input('fname') != null) {
                first = request.input('fname')
            }

            let last = ""
            if (request.input('lname') != null) {
                last = request.input('lname')
            }

            console.log(first)
            console.log(last)
            console.log(request.input('email'))
            console.log(uImg)

            let newUser = await User.create({
                fname: first,
                lname: last,
                email: request.input('email'),
                profile_img: uImg,
                login_source: 'email',
                info: '',
                password: request.input('password')
            })

            await auth.login(newUser)
            
        } catch(error) {
            session
            .withErrors([
                {field: 'database', message: "error adding user to database"}
            ])
            .flashExcept(['password'])

            return response.redirect('back')
        }

        session.flash({notification: 'registered'})
        return response.redirect('/')
    }

thanks for looking.

From what I’m reading, it seems I may have to use a third party form parser like multer to get the body data and not just the form data from a multipart. That seems poorly implemented if that is the case but whatever.

It’s either I do that or i use {{script}} to link to a javascript file that grabs the file input directly and removes it from the form. injecting javascript into a view like that has to be against best practices but I’m not really sure what my other options are.

Depends on your use-case and how big files you want to upload. Do you want to upload on the AdonisJS proxy or in S3? (not sure if you can do any kind of validation in there tho)
Easiest is to keep autoprocess enabled in config and use
https://adonisjs.com/docs/4.1/file-uploads

it kinda makes no sense to upload them to the server just to send them to s3. in the end i did the second option and uploaded it in the background as soon as they chose it. also had to block the form submission by disabling the submit button and changing its text to ‘loading…’ until the image is done uploading. works though.

Aah sorry, I missed this sentence somehow

I just cant get the other form data if i use processManually

You can use request.multipart.field

const body = {};

await request.multipart.field((name, value) => {
  body[name] = value;
});

Longer thread about it starts from here

There’s no need to save it to server indeed if you just want to send it off to s3 bucket

2 Likes