transloadit / uppy

The next open source file uploader for web browsers :dog:
https://uppy.io
MIT License
29.02k stars 2k forks source link

Cannot upload files from Google Drive when running Uppy 4 and Companion 5 #5465

Closed xulww closed 1 week ago

xulww commented 1 week ago

Initial checklist

Link to runnable example

No response

Steps to reproduce

Run the code below with the following packages installed: uppy: 4.3.0 @uppy/aws-s3: 4.1.0 @uppy/google-drive: 4.1.0 @uppy/companion: 5.1.0

Client integration

import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";
import axios from "axios";
import { v4 as uuidv4 } from "uuid";
import queryString from "query-string";

import { captureError } from "~/shared/utils";

// Uppy
import Uppy from "@uppy/core";
import { Dashboard } from "@uppy/react";
import AwsS3 from "@uppy/aws-s3";
import GoogleDrive from "@uppy/google-drive";

import "uppy/dist/uppy.min.css";
import "./uppy-custom.less";

const addNewVideo = async ({ filename, originalName }) => {
   // make HTTP request to upload video to server
};

function UploadVideo({  onComplete }) {
    const [isUppyReady, setIsUppyReady] = useState(false);

    const uppyRef = useRef(null);
    const originalNameRef = useRef("");
    const fileNameRef = useRef("");

    const onBeforeUpload = (files) => {
        Object.keys(files).forEach((key) => {
            const file = files[key];
            const { name } = file;

            originalNameRef.current =
                name.substr(0, name.lastIndexOf(".")) || name;

            file.name = [uuidv4(), file.extension].filter(Boolean).join(".");
        });
    };

    useEffect(() => {
        const restrictions = {
            maxNumberOfFiles: 1,
            minNumberOfFiles: 1,
            allowedFileTypes: ["video/*", "video/mp4"],
        };

        uppyRef.current = new Uppy({
            restrictions,
            autoProceed: true,
            onBeforeUpload,
        });

        uppyRef.current.use(AwsS3, {
            shouldUseMultipart() {
                return false;
            },

            async getUploadParameters(file) {
                // make HTTP request to get additional info from the server

                return {
                    method,
                    url,
                    fields,
                };
            },
        });

        uppyRef.current.use(GoogleDrive, {
            companionUrl: "https://companion.example.com/companion",
        });

        uppyRef.current.on("complete", async (result) => {
            const { successful } = result;
            const [file] = successful;

            if (!file) return;

            const video = await addNewVideo({
                filename: fileNameRef.current,
                originalName: originalNameRef.current,
            });

            onComplete(video);
        });

        uppyRef.current.on("error", (error) => {
            captureError(error);
        });

        setIsUppyReady(true);
    }, [onComplete]);

    if (!isUppyReady) return null;

    return <Dashboard uppy={uppyRef.current} plugins={["GoogleDrive"]} />;
}

Server integration (self-hosted Companion instance)

import express from "express";
import bodyParser from "body-parser";
import session from "express-session";
import companion from "@uppy/companion";
import path from "path";

const APP_SECRET = process.env.APP_SECRET;
const COMPANION_GOOGLE_KEY = process.env.COMPANION_GOOGLE_KEY;
const COMPANION_GOOGLE_SECRET = process.env.COMPANION_GOOGLE_SECRET;

const __dirname = path.resolve();

const app = express();
app.use(bodyParser.json());
app.use(
  session({ secret: APP_SECRET, resave: false, saveUninitialized: true })
);

const options = {
  providerOptions: {
    drive: {
      key: COMPANION_GOOGLE_KEY,
      secret: COMPANION_GOOGLE_SECRET,
    },
  },
  server: {
    host: "companion.example.com",
    protocol: "https",
    path: "/companion",
  },
  filePath: `${__dirname}/uploads`,
  secret: APP_SECRET,
};

const { app: companionApp } = companion.app(options);
app.use("/companion", companionApp);

const server = app.listen(3020);
companion.socket(server, options);

Expected behavior

The files should be automatically uploaded to the server which was the observed behaviour before migrating the code to Uppy 4 and Companion 5.

Actual behavior

The files are not uploaded and the following error is logged on the server running the Companion instance:

[error] bc14c2f3 uploader.error TypeError: s3: bucket key must be a string or a function resolving the bucket string

The desired Google Drive file does reach the onBeforeUpload function but for some reason doesn't reach the getUploadParameters function. Something between those two functions breaks the flow of uploading a file.

Murderlon commented 1 week ago

Hi, it says it clearly in the error? You are not setting the s3 bucket key. You are using S3 on the client for Uppy so Companion will also try to upload to S3. Make sure you add your S3 credentials in Companion.

https://uppy.io/docs/companion/#s3

Murderlon commented 1 week ago

Note that you are using Uppy in React incorrectly. I would refrain from useRef + useEffect and instead follow our recommendations from the docs.

xulww commented 1 week ago

I don't see how our Uppy implementation in React is "incorrect" but we will definitely change it so it's aligned with the docs. As I noted above, there weren't any issues in the previous major versions. The problems started to surface after we followed the migration guides. If adding additional S3 settings to the Companion server is now required, this should be included in the migration guides. Thank you for the quick response!

Murderlon commented 6 days ago

I don't understand how your Companion uploaded to AWS before without having any credentials? Are you sure you didn't change uploader on the client as well?

xulww commented 5 days ago

I also don't understand what went wrong with the new versions. The code I've included above is the code we've been using for the past 2+ years and we haven't had any issues until now. To be fair, I'm not sure if the files were being uploaded to S3 from the Companion server directly (most likely that wasn't the case). I also don't really understand what is the point of providing S3 credentials to the Companion instance now. Isn't the upload flow something like Companion -> Google Drive -> Companion -> Uppy (client) -> S3? After all, the docs describe Companion as:

an open source server application which takes away the complexity of authentication and the cost of downloading files from remote sources, such as Instagram, Google Drive, and others...

which sounds like it should only help users access the files (and then send it to the Uppy client instance?). I might be wrong tho, the whole thing is a bit of a black box to me and many others as it seems.

xulww commented 5 days ago

For the record, we reverted to Uppy version 3 and Companion version 4 and everything works again (using the code above)

Murderlon commented 1 day ago

No, the point of Companion is to not send any files to the client. The reason it exists is so clients save bandwidth. It only sends back metadata to the client to display a list of files and the client then chooses which files are going to be downloaded inside Companion.

Have you tried adding the credentials to see if it works?

@mifi did something change that was missed in the migration guide?