FileUpload doesn't work on mobile Chrome-based browsers (Chrome, Brave) on Android
See original GitHub issueDescribe 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:
- Go to https://nocodefunctions.com/nocodeapp-web-front-1.0/umigon/umigon.html
- Click on ‘Choose file’ and choose a txt, csv or xlsx file
- Click on ‘Upload’
- 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:
- Created 2 years ago
- Comments:12 (10 by maintainers)
Top 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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
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
Thanks for testing @tsobis !