Upload a file to temporary folder with a Yesod application: encoding issue

48 Views Asked by At

I'm trying to do a Yesod web app allowing to upload a file and to save it in the temporary folder. I'm using Base64 encoding/decoding. When I upload a text file with this content:

copie carte d'identité
téléphone
autocertfication fiscale
signature

then the content of the file I get in the temporary folder is:

u«Zµìmþ™ZŠvÚ±î¸copie carte d'identité
téléphone
autocertfication fiscale
signature

The input file is UTF8-encoded.

Here is how I handle the file upload with JavaScript:

$("#file").on("change", function() {
    let file = this.files[0];
    let fileReader = new FileReader(); 
    fileReader.readAsDataURL(file);
    fileReader.onload = function() {
        let base64 = fileReader.result;
        $.ajax({
            contentType: "application/json",
            processData: false,
            url: "@{FileR}",
            type: "PUT",
            data: JSON.stringify({
                _filename: file.name, 
                _base64: base64
            }),
            success: function(result) {
                // not important
            },
            dataType: "text"
        });
    }; 
    fileReader.onerror = function() {
        alert(fileReader.error);
    }; 
});

In Haskell, I have:

import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Char8 as BC
import System.IO.Temp ( getCanonicalTemporaryDirectory )

base64ToFile :: String -> FilePath -> IO FilePath
base64ToFile b64string fileName = do
    let bstring = B64.decodeLenient (BC.pack b64string)
    tmpDir <- getCanonicalTemporaryDirectory
    let filePath = tmpDir ++ "/" ++ fileName
    B.writeFile filePath bstring 
    return filePath

data File = File {
    _filename :: String,
    _base64   :: String
} deriving (Show, Generic)

instance FromJSON File

b64FileToFile :: File -> IO FilePath
b64FileToFile file = base64ToFile (_base64 file) (_filename file)

putFileR :: Handler String
putFileR = do
    file <- requireCheckJsonBody :: Handler File
    liftIO $ b64FileToFile file 

I don't know where is the source of the issue, whether this is in the JavaScript step or in the Haskell step. I want to be able to upload a text file, a CSV file, a XLSX file, or an image file.

1

There are 1 best solutions below

0
Stéphane Laurent On

One has to remove the MIME type from the Base64 string, that is, to take only the part after the comma:

let base64 = fileReader.result.split(",")[1];