question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

FileUpload doesn't work on mobile Chrome-based browsers (Chrome, Brave) on Android

See original GitHub issue

Describe the defect FileUpload doesn’t work on mobile Chrome-based browsers (Chrome, Brave) on Android

Reproducer Live app can be tested here: https://nocodefunctions.com/nocodeapp-web-front-1.0/umigon/umigon.html -> file upload works on desktop and mobile - just FIrefox -> file upload does not work on mobile - Chrome and Brave browsers

Environment:

  • PF Version: 10.0
  • JSF + version: 2.3, Mojarra version: the one coming wih Payara Server 5
  • Affected browsers: Chrome based browsers on mobile

To Reproduce Steps to reproduce the behavior:

  1. Go to https://nocodefunctions.com/nocodeapp-web-front-1.0/umigon/umigon.html
  2. Click on ‘Choose file’ and choose a txt, csv or xlsx file
  3. Click on ‘Upload’
  4. See error: file is not uploaded

Expected behavior The file should be uploaded

Example XHTML

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:o="http://omnifaces.org/ui"
      xmlns:p="http://primefaces.org/ui">

    <h:head>
        <h:outputStylesheet library="css" name="primeflex.min.css" />
        <h:outputStylesheet library="css" name="primeflex.min.css.map" />
    </h:head>

    <h:body>
        <p:growl id="messages" showDetail="true"/>
        <h:form>
            <p:menubar>
                <p:menuitem value="Home" action="#{umigonBean.logout}" icon="pi pi-home"/>
            </p:menubar>
        </h:form>
        <h1 class="title">Umigon: sentiment analysis for social media</h1>
        <div class="p-grid" >
            <div class="p-col-9">
                <div class="p-grid p-flex-column">
                    <div id= "input" class="p-col">
                        <!--BUTTON TO CHOOSE AN INPUT FILE-->
                        <h:form enctype="multipart/form-data">
                            <p:fileUpload value="#{umigonBean.file}" onstart="PF('startButton').disable()" oncomplete="PF('startButton').enable()" label="Choose a file" mode="advanced" auto="false" skinSimple="true" sizeLimit="10000000"
                                          process="@this" listener="#{umigonBean.handleFileUpload}" update="messages form1 excelParamsOuter :notifications"
                                          allowTypes="/(\.|\/)(csv|xlsx|txt)$/">                                
                            </p:fileUpload>
                            <!--BUTTON TO RUN THE ANALYSIS-->
                        </h:form>
                        <br/>
                        <div class="p-formgroup-inline">
                            <!--BUTTON TO RUN THE ANALYSIS-->
                            <h:form id="form1" enctype="multipart/form-data">
                                <p:commandButton value="Run the analysis" onclick="PF('pbAjaxLong').start();PF('startButton').disable()"
                                                 widgetVar="startButton" action="#{umigonBean.runAnalysis}" disabled="#{(umigonBean.runButtonDisabled)}" styleClass="btn btn-primary" update="messages @this :form2:seeResults :notifications"/>

                                <p:progressBar widgetVar="pbAjaxLong" ajax="true" value="#{umigonBean.progress}"
                                               labelTemplate="{value}%" styleClass="p-mt-3" global="false" interval="500">
                                    <p:ajax event="complete" oncomplete="PF('startButton').disable()" listener="#{umigonBean.onComplete}" update="messages :form2:seeResults"/>
                                </p:progressBar>
                            </h:form>
                            <p:spacer width="10"/>
                            <h:form id="form2">
                                <p:outputLabel id="seeResults">
                                    <p:commandButton value="See the results" action="#{umigonBean.seeResults}" rendered="#{umigonBean.renderSeeResultsButton}" styleClass="p-mr-2"/>
                                </p:outputLabel>
                            </h:form>
                        </div>
                    </div>
                    <div id= "test" class="p-col">
                        <p:panel header="No file? Test it here">
                        </p:panel> 
                    </div>
                    <div class="p-col">
                        <p:panel header="Optional parameters">
                            <p:selectBooleanCheckbox value="#{umigonBean.headers}" itemLabel="My input file has headers"/>
                            <br/>
                            <br/>
                            <p:selectOneListbox id="lang" value="#{umigonBean.selectedLanguage}" style="width: 50px !important">
                                <f:selectItem itemLabel="English" itemValue="en">
                                    <!--                                    <p:column>
                                        <span class="flag flag-en" style="width: 30px; height: 20px"/>
                                    </p:column>
                                    <p:column>
                                        <h:outputText value="English"/>
                                    </p:column>-->
                                </f:selectItem>
                                <f:selectItem itemLabel="French" itemValue="fr">
                                    <!--                                    <p:column>
                                                                            <span class="flag flag-fr" style="width: 30px; height: 20px"/>
                                                                        </p:column>
                                                                        <p:column>
                                                                            <h:outputText value="French"/>
                                                                        </p:column>-->
                                </f:selectItem>
                            </p:selectOneListbox>
                            <p:outputPanel id="excelParamsOuter">                                
                                <p:outputPanel id="excelParamsInner" rendered ="#{umigonBean.excelFile}">
                                    <p:divider align="left">
                                        <div class="p-d-inline-flex p-ai-center">
                                            <i class="pi pi-file-excel p-mr-2"/>
                                            <b>Parameters for Excel input files</b>
                                        </div>
                                    </p:divider>
                                    <p:selectOneMenu value="#{umigonBean.selectedSheet}" dynamic="true">
                                        <f:selectItem itemLabel="Select the sheet" itemValue=""/>
                                        <f:selectItems value="#{umigonBean.sheets}"/>
                                    </p:selectOneMenu>
                                    <p:spacer width ="20"/>
                                    <p:selectOneMenu id="option" value="#{umigonBean.selectedColumn}">
                                        <f:selectItem itemLabel="column A" itemValue="A"/>
                                        <f:selectItem itemLabel="column B" itemValue="B"/>
                                        <f:selectItem itemLabel="column C" itemValue="C"/>
                                    </p:selectOneMenu>
                                </p:outputPanel>
                            </p:outputPanel>
                        </p:panel> 
                    </div>
                </div>
            </div>
            <div id="thirdcol" class="p-col-3">
                <p:panel header="Log">
                    <p:scrollPanel id="scrollPanel" mode="native" style="height:100%">
                        <h:dataTable id="notifications" value="#{logBean.notifications}" var="notification">
                            <h:column>#{notification.message}</h:column>
                        </h:dataTable>
                        <h:form>
                            <!--wrong error from netbeans, do not add onmessage in o:socket when there is a f:ajax inside-->
                            <o:socket channel="push" scope="session">
                                <f:ajax event="updateNotifications" render=":notifications" />
                            </o:socket>
                        </h:form>
                    </p:scrollPanel>
                </p:panel>
            </div>
        </div>
    </h:body>
</html>

Example Bean

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package net.clementlevallois.nocodeapp.web.front.backingbeans;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.stream.Collectors.toList;
import java.util.stream.Stream;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.Part;
import net.clementlevallois.nocodeapp.web.front.http.SendReport;
import net.clementlevallois.nocodeapp.web.front.io.ExcelReader;
import net.clementlevallois.nocodeapp.web.front.io.ExcelSaver;
import net.clementlevallois.nocodeapp.web.front.logview.NotificationService;
import net.clementlevallois.umigon.controller.UmigonController;
import net.clementlevallois.umigon.model.Document;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellReference;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.file.UploadedFile;
import org.primefaces.model.file.UploadedFiles;

/**
 *
 * @author LEVALLOIS
 */
@Named
@SessionScoped
@MultipartConfig
public class UmigonBean implements Serializable {

    private UploadedFile file;
    private Part fileOmniface;
    private UploadedFiles files;
    private Integer progress;
    private Map<Integer, Document> sortedMapOfResults;
    private String[] sentiments = new String[]{"positive", "negative", "neutral"};
    private List<String> sheets;
    private String selectedSheet;
    private String selectedColumn;
    private String selectedLanguage = "en";
    private Boolean headers = true;
    private Boolean excelFile = false;
    private Boolean runButtonDisabled = true;
    private List<String> sheetNames;
    private StreamedContent fileToSave;
    private Boolean renderSeeResultsButton = false;
    private byte[] uploadedFileContents;

    @Inject
    NotificationService service;

    public Integer getProgress() {
        return progress;
    }

    public void setProgress(Integer progress) {
        this.progress = progress;
    }

    public void onComplete() {
    }

    public void cancel() {
        progress = null;
    }

    public UploadedFile getFile() {
        return file;
    }

    public void setFile(UploadedFile file) {
        this.file = file;
    }

    public UploadedFiles getFiles() {
        return files;
    }

    public void setFiles(UploadedFiles files) {
        this.files = files;
    }

    public Part getFileOmniface() {
        return fileOmniface;
    }

    public void setFileOmniface(Part fileOmniface) {
        this.fileOmniface = fileOmniface;
    }

    public void upload() {
        if (fileOmniface != null) {
            service.create("⭐ file successfully uploaded");

            String toString = Paths.get(fileOmniface.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
            System.out.println("path: " + toString);
            try (InputStream input = fileOmniface.getInputStream()) {
            } catch (IOException e) {
                service.create("🥺 file could not be uploaded");
            }
            fileOmniface = null;
        }

    }

    public String handleFileUpload(FileUploadEvent event) {
        System.out.println("handle file upload event received");
        //clearing previous results if any;
        sortedMapOfResults = new TreeMap();
        progress = 0;
        service.create("⭐ file successfully uploaded");
        file = event.getFile();
        uploadedFileContents = file.getContent();
        System.out.println("filename is: " + event.getFile().getFileName());
        if (file != null && file.getFileName().endsWith("xlsx")) {
            excelFile = true;
            service.create("👩‍🏭 check extra parameters for Excel at the bottom");
            try {
                sheetNames = ExcelReader.getListOfSheets(file);
            } catch (IOException ex) {
                Logger.getLogger(UmigonBean.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            excelFile = false;
        }
        runButtonDisabled = false;
        return "";
    }

    public String runAnalysis() {
        BufferedReader br = null;
        List<String> lines;
        if (file == null) {
            service.create("🥺 upload of the file failed");
            System.out.println("file not found");
            return "";
        }

        try {
            service.create("👓 reading the file...");
            this.progress = 3;
            if (file.getFileName().endsWith("xlsx")) {
                lines = readLinesFromWorkBook(uploadedFileContents, selectedSheet, selectedColumn, headers);
            } else {
                br = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8));
                lines = br.lines().collect(toList());
                br.close();
            }
            service.create("the file has " + lines.size() + " lines 🎰");
            service.create("🏃 starting the analysis");
            if (selectedLanguage == null || selectedLanguage.isBlank()) {
                selectedLanguage = "en";
            }
            call(lines, selectedLanguage);
            service.create("✨ analysis is complete");
            renderSeeResultsButton = true;
            runButtonDisabled = true;

        } catch (IOException ex) {
            Logger.getLogger(UmigonBean.class.getName()).log(Level.SEVERE, null, ex);
            return "";
        } catch (Exception ex) {
            Logger.getLogger(UmigonBean.class.getName()).log(Level.SEVERE, null, ex);
            return "";
        }
        return "";

    }

    public Map<Integer, Document> call(List<String> inputLines, String lang) throws Exception {
        ConcurrentMap<Integer, Document> docs = new ConcurrentHashMap();
        UmigonController umigonController = new UmigonController();
        int maxRecords = inputLines.size();
        umigonController.initializeClassifier(lang);
        Integer[] count = {0};
        Integer[] prevProgress = {15};
        Stream<String> stream;
        stream = inputLines.parallelStream();
        stream.forEach(text -> {
            try {
                Document doc = umigonController.runSingleLineAfterClassifierInitialization(text);
                count[0]++;
                docs.put(count[0], doc);
                if (count[0] % 200 == 0) {
                    float progressFloat = 15 + (float) count[0] / maxRecords * 85;
                    int progressUmigon = (int) progressFloat;
                    if (progressUmigon != prevProgress[0]) {
                        prevProgress[0] = progressUmigon;
                        System.out.println(progressUmigon);
                        this.progress = progressUmigon;
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });
        this.progress = 100;

        sortedMapOfResults = new TreeMap();
        sortedMapOfResults.putAll(docs);

        return sortedMapOfResults;
    }

    public String seeResults() {
        if (sortedMapOfResults != null && sortedMapOfResults.size() > 0) {
            return "results?faces-redirect=true";
        } else {
            service.create("🤔 no result found?");
            return "";
        }
    }

    public Boolean getRenderSeeResultsButton() {
        return renderSeeResultsButton;
    }

    public void setRenderSeeResultsButton(Boolean renderSeeResultsButton) {
        this.renderSeeResultsButton = renderSeeResultsButton;
    }

    public List<Document> getSortedMapOfResults() {
        return sortedMapOfResults.entrySet().stream().map(w -> w.getValue()).collect(toList());
    }

    public void setSortedMapOfResults(Map<Integer, Document> sortedMapOfResults) {
        this.sortedMapOfResults = sortedMapOfResults;
    }

    public String[] getSentiments() {
        return sentiments;
    }

    public void setSentiments(String[] sentiments) {
        this.sentiments = sentiments;
    }

    public List<String> getSheets() {
        return sheetNames;
    }

    public void setSheets(List<String> sheets) {
        this.sheets = sheets;
    }

    public String getSelectedSheet() {
        return selectedSheet;
    }

    public void setSelectedSheet(String selectedSheet) {
        this.selectedSheet = selectedSheet;
    }

    public String getSelectedColumn() {
        return selectedColumn;
    }

    public void setSelectedColumn(String selectedColumn) {
        this.selectedColumn = selectedColumn;
    }

    public Boolean getHeaders() {
        return headers;
    }

    public void setHeaders(Boolean headers) {
        this.headers = headers;
    }

    public String getSelectedLanguage() {
        return selectedLanguage;
    }

    public void setSelectedLanguage(String selectedLanguage) {
        this.selectedLanguage = selectedLanguage;
    }

    public Boolean getExcelFile() {
        return file != null && file.getFileName().endsWith("xlsx");
    }

    public void setExcelFile(Boolean excelFile) {
        this.excelFile = excelFile;
    }

    public Boolean getRunButtonDisabled() {
        return runButtonDisabled;
    }

    public void setRunButtonDisabled(Boolean runButtonDisabled) {
        this.runButtonDisabled = runButtonDisabled;
    }

    public String signal(String status, String sentiment) {
        SendReport sender = new SendReport();
        sender.init(status + " - should not be " + sentiment);
        sender.start();
        for (Map.Entry<Integer, Document> entry : sortedMapOfResults.entrySet()) {
            if (entry.getValue().getText().equals(status)) {
                entry.getValue().setFinalNote(666);
            }
        }
        return "";
    }

    public void dummy() {
    }

    public StreamedContent getFileToSave() {
        return ExcelSaver.createStreamedContent(sortedMapOfResults);
    }

    public void setFileToSave(StreamedContent fileToSave) {
        this.fileToSave = fileToSave;
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "/index?faces-redirect=true";
    }

    public List<String> readLinesFromWorkBook(byte[] byteArrayFileContent, String worksheetName, String columnLetter, boolean hasHeaders) {
        if (byteArrayFileContent == null) {
            return new ArrayList();
        }
        List<String> lines = new ArrayList();
        InputStream inputStream = new ByteArrayInputStream(byteArrayFileContent);
        try (Workbook wb = WorkbookFactory.create(inputStream)) {
            Sheet sheet;
            if (worksheetName == null) {
                sheet = wb.getSheetAt(0);
            } else {
                sheet = wb.getSheet(worksheetName);
            }
            if (columnLetter == null) {
                columnLetter = "A";
            }
            int maxRow = sheet.getLastRowNum();

            Row row;
            CellReference cellRef = new CellReference(columnLetter + "1");
            int col = cellRef.getCol();
            int startRow = hasHeaders ? 1 : 0;
            Cell cell;
            int countEmptyCells = 0;
            int prevProgress = 3;
            for (int i = startRow; i < maxRow; i++) {
                row = sheet.getRow(i);
                cell = row.getCell(col);
                if (cell == null || !cell.getCellType().equals(CellType.STRING)) {
                    lines.add("blank cell at row " + i);
                    countEmptyCells++;
                } else {
                    lines.add(cell.getStringCellValue());
                }
                float progressFloat = 3 + (float) i / maxRow * 12;
                int progressInt = (int) progressFloat;
                if (progressInt != prevProgress) {
                    prevProgress = progressInt;
                    this.progress = progressInt;
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(ExcelReader.class.getName()).log(Level.SEVERE, null, ex);
        }
        return lines;
    }

}

Example web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
    <!--    <context-param>
        <param-name>primefaces.THEME</param-name>
        <param-value>bootstrap</param-value>
    </context-param>-->
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Production</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    <context-param>
        <param-name>org.omnifaces.SOCKET_ENDPOINT_ENABLED</param-name>
        <param-value>true</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    <context-param>	
        <param-name>primefaces.UPLOADER</param-name>	
        <param-value>auto</param-value>
    </context-param>
    <mime-mapping>
        <extension>ttf</extension>
        <mime-type>application/font-ttf</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>eot</extension>
        <mime-type>application/font-eot</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>woff</extension>
        <mime-type>application/font-woff</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>map</extension>
        <mime-type>application/json</mime-type>
    </mime-mapping>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>    
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <!-- Enable partial submit in PrimeFaces - this reduces the network traffic -->
    <context-param>
        <param-name>primefaces.SUBMIT</param-name>
        <param-value>partial</param-value>
    </context-param>

    <!-- Move above the fold scripts to the bottom (end of body).
    This is a huge improvement of the visible rendering and removes flickering between navigations. -->
    <context-param>
        <param-name>primefaces.MOVE_SCRIPTS_TO_BOTTOM</param-name>
        <param-value>true</param-value>
    </context-param>
</web-app>

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:12 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
tsobiscommented, May 3, 2021

Tested also on Android 9 chrome 79.0.3945.116 Upload works on page https://nocodefunctions.com/nocodeapp-web-front-1.0/umigon/umigon.html

1reaction
mellowarecommented, May 3, 2021

Thanks for testing @tsobis !

Read more comments on GitHub >

github_iconTop Results From Across the Web

Brave doesn't download files after specifying download ...
Downloads in Firefox work fine, while Chromium has the same issue – since Brave is based on Chromium it seems this is a...
Read more >
file upload does not work on android browser #429 - GitHub
I have been working with this javascript. I need it to work for major browsers including android browser but can't seem to get...
Read more >
(Android) Some downloaded images from Chrome and Brave ...
When i downloands images in these two browser, sometimes images do not shows in gallery. When i look downloand folder in file manager, ......
Read more >
Unable to upload files in chrome/firefox using files app in ...
Previously when I updated my phone to MIUI 12.1 (also android 11), I was facing the same problem. So i downgraded to android...
Read more >
How To Fix YouTube Video Uploading in Chrome Browser on ...
In this video I am teaching you How To Fix YouTube Video Uploading in Chrome Browser on Mobile. Brave Browser For Android ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found