2

I would like to write a service for converting files from one format to another (something similar at https://convertio.co/mp4-gif/) . I already have a file upload button that allows the user to upload a file from their device to the site. There is a box that displays the file name, size, desired format for conversion and a button to convert. By pressing the convert button (ConvertFileButton) a request is sent to the backend. And now I have a question: how can I get an already converted file from the backend and display it for the user (so that the user can download a new converted file)

Now in order:

below is the "CONVERT" button component that sends a request to the backend

export default function ConvertFileButton() {

  const { file } = useContext(AppContext)
  const formData = new FormData();
  formData.append('file', file);

  return (
    <MyButton onClick={() => { fetch('http://localhost:5000/transcript', 
    { method: 'post', body: formData }); }}>CONVERT</MyButton>
  )
}

Below I will present three functions on the backend that process the request. The backend is written in Python using Flask.

The first function to which the file is sent and in which the conversion takes place

    @mod.route('/transcript', methods=['POST', "GET"])
def create_transcript_task():
   return redirect(url_for('backend.status_page') + f'?id={task_id}')

then you can get the finished converted file

@mod.route('/get', methods=['GET'])
def get_result():
    return send_file(file_path)

Thus, I will explain the question: how can I display this finished file again in React, so that the user can download a new converted file.

As I understand it, you need to create a component in which to transfer this new file. But I don't understand how to do it. Perhaps you can help me

    export default function DisplayConvertedFile() {

    return (
      // some code //
    )
  }
Paul
  • 53
  • 3
  • 21
  • I don't know `reactjs` but in normal JavaScript you could convert image to string `base64` and put `` – furas Sep 01 '22 at 08:33
  • Use ```send_file(file_path, as_attachment=True)```. This will make the file downloadable instead of trying to display the contents directly on the web page – NoCommandLine Sep 01 '22 at 14:20
  • @NoCommandLine I don't need to display the contents of the new file on the page. I need to display its name on the page (with the new format) and make it possible to download this file. Can you help me with the code? So far it's very difficult for me. – Paul Sep 01 '22 at 20:29
  • Did you try the change I suggested? – NoCommandLine Sep 02 '22 at 14:24
  • @NoCommandLine Yes, I tried, but I still did not understand where and in which component or function to add this. – Paul Sep 02 '22 at 14:45
  • Your code already has ```return send_file(file_path)```. Just replace the send_file bit – NoCommandLine Sep 02 '22 at 16:07
  • @NoCommandLine I'm afraid you misunderstood the condition: I need to return the converted file from the backend and display it in the React component so that the user can download it – Paul Sep 04 '22 at 13:16
  • 1
    I would recommend you to decouple your backend logic. So you will convert the file, store it on your server with a defined TTL, give back in the response the link to file and the TTL value. Then in the front just use a [download link](https://www.w3schools.com/TAGS/att_a_download.asp) like ``. It has the advantage to handle bigger file (no long response to stream the file), have a cache if the user reload the page, use native file transfer with http – johannchopin Sep 05 '22 at 09:02
  • @johannchopin Thanks for the recommendations, I appreciate them. But could you help me with the code. I have not been familiar with React for so long, and some points are difficult for me. I have been studying this task for about a week, but have not progressed. How do I need to implement the DisplayConvertedFile component to make it work? – Paul Sep 05 '22 at 11:02
  • Related [Sending a file from Flask using send_file to a React frontend returns a string of weird characters](https://stackoverflow.com/q/63801945/8746648) – asynts Sep 05 '22 at 12:55
  • @asynts Yes, this is something closest to my question. Thank you. Could you help me adapt this to my code? – Paul Sep 06 '22 at 09:55

1 Answers1

3

We can simplify the server as follows:

from flask import Flask, send_file

app = Flask(__name__)

@app.route('/get', methods=['GET'])
def get_result():
    response = send_file("hello.jpg")
    response.headers.add("Access-Control-Allow-Origin", "*")

    return response

app.run(debug=True)

This will serve the image at 127.0.0.1:5000/get similar to your situation (it assumes that there is an image called hello.jpg. Now we can display this in React by creating an URL from the data, this can be done as follows:

<html>
    <head>
        <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    </head>

    <body>
        <div id="root"></div>
    </body>

    <script defer>
        function DisplayComponent(props) {
            const [dataUrl, setDataUrl] = React.useState(null);

            // There are several ways to convert the result of 'fetch' to an URL.
            // Refer to: https://stackoverflow.com/q/7650587/8746648
            fetch("http://127.0.0.1:5000/get")
                .then(response => response.blob())
                .then(blob => URL.createObjectURL(blob))
                .then(newDataUrl => setDataUrl(newDataUrl));

            return React.createElement("img", { src: dataUrl });
        }


        console.log(document.getElementById("root"));

        const root = ReactDOM.createRoot(document.getElementById("root"));
        root.render(React.createElement(DisplayComponent));
    </script>
</html>

I used the regular syntax here, because that is easier to reproduce.


EDIT:

To clear up some confusion, here is the same thing but in the JSX syntax. I also added a button to initiate the request since this caused confusion.

import {useState} from "react";

function DisplayComponent(props) {
    const [dataUrl, setDataUrl] = useState(null);

    function onClick() {
        // There are several ways to convert the result of 'fetch' to an URL.
        // Refer to: https://stackoverflow.com/q/7650587/8746648
        fetch("http://127.0.0.1:5000/get")
            .then(response => response.blob())
            .then(blob => URL.createObjectURL(blob))
            .then(newDataUrl => setDataUrl(newDataUrl));
    }

    return (
        <div>
            <button onClick={onClick}>Fetch</button>
            <img src={dataUrl} />
        </div>
    );
}

export default DisplayComponent;

EDIT 2:

I assumed you were dealing with an MP4 to GIF conversion because of the link in the question. If you have a general file, like a sound file you can just let the user download it:

// Copied from: https://stackoverflow.com/a/45905238/8746648
function download(dataurl, filename) {
  const link = document.createElement("a");
  link.href = dataurl;
  link.download = filename;
  link.click();
}

function onClick() {
    // There are several ways to convert the result of 'fetch' to an URL.
    // Refer to: https://stackoverflow.com/q/7650587/8746648
    fetch("http://127.0.0.1:5000/get")
        .then(response => response.blob())
        .then(blob => URL.createObjectURL(blob))
        .then(newDataUrl => download(newDataUrl, "filename.jpg"));
}
asynts
  • 2,213
  • 2
  • 21
  • 35
  • Thank you for paying attention to my question. Unfortunately this solution doesn't work for me. As soon as I run this code, requests are constantly sent to the server and slow down the browser. I can't even select the desired file to convert – Paul Sep 06 '22 at 09:53
  • For simplicity, I just put the `fetch` into the component directly. You could move this logic into an `onClick` handler then it won't fire all the time. The point here is just that you can convert the data into an URL and then pass it to the `src` attribute of an ``. – asynts Sep 06 '22 at 10:14
  • Can you show me this with an example of my code? – Paul Sep 12 '22 at 12:02
  • What is unclear? – asynts Sep 12 '22 at 12:59
  • It's not clear how to apply your code to my code. And also how to transfer the logic to onClick (what you mentioned in the previous comment) – Paul Sep 12 '22 at 13:07
  • You create a function and bind it to `onClick`, in that handler you make the network request with `fetch` and once the result comes back you use `setDataUrl` (this comes from `useState`) to update the component. I can edit this to use the JSX syntax instead, if you prefer. – asynts Sep 12 '22 at 13:16
  • I would be very grateful if you edit – Paul Sep 12 '22 at 13:18
  • I've edited the answer to include the JSX syntax. I hope that clears things up. – asynts Sep 12 '22 at 13:30
  • Yes, thanks, that made it a little clearer. Tell me more: I do not convert the picture, but the sound file. Does it somehow affect the code or is there no difference? – Paul Sep 12 '22 at 13:40
  • Now I understand what you mean, I updated the answer to download the file when the button is pressed. – asynts Sep 12 '22 at 13:50
  • Sorry for the long answer. That is, I need to place function download and function onClick () in the DisplayComponent component – Paul Sep 13 '22 at 08:06
  • You can put the `download` function wherever you want. If you use it in multiple places you could put it into another file, or you could put it with that component. That doesn't really matter. – asynts Sep 13 '22 at 08:07
  • I apologize for so many questions, but I just want to understand this topic. How then should the DisplayComponent look like so that the user can click on the button and download the converted file? – Paul Sep 13 '22 at 08:12
  • Something like ``. I think you should brush up your knowledge of React a bit. [The official documentation is pretty good.](https://reactjs.org/docs/hello-world.html) – asynts Sep 13 '22 at 09:56
  • The fact is that this is not my main programming language. And here you also need to connect the back and front. Therefore, questions arise. In any case, thank you for your advice and help! – Paul Sep 13 '22 at 10:16