Folder Upload with Angular Js - javascript

I understand that file upload is possible with Angular Js. But I researched and found no provisions for an entire folder to be uploaded.
I am working on ASP.NET Core.
Is folder uploading possible with AngularJS. Alternatives are welcome as well.
Also I am wondering what is the equivalent of FolderBrowserDialog in ASP.NET Core.

Modern browsers support the File and Directory Entries API which allows handling of directories (folders). At the time of writing, this works in Chrome, Firefox, Edge and Safari TP (no old IE support).
You have at least two ways to implement this (brownie points for doing both for best UX!).
Method 1: Using file input
Give the standard input element the power to handle directories via the webkitdirectory attribute:
<input
#folderInput
type="file"
(change)="filesPicked(folderInput.files)"
webkitDirectory
>
Note: the above HTML snippet assumes Angular 2+, but you can easily do the same thing in Angular 1
folderInput.files will be a flat list of all files within the folder, no need to recursively walk the tree or anything like that. Just iterate through the files in a simple loop like this:
function filesPicked(files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
const path = file.webkitRelativePath.split('/');
// upload file using path
...
}
}
The property webkitRelativePath, as the name suggests, contains a path of the file relative to the ancestor directory the user selected.
Method 2: Using drag and drop
Another part of the same File and Directory Entries API is DataTransferItem.webkitGetAsEntry() which returns a FileSystemFileEntry or FileSystemDirectoryEntry. If it's a directlry, you can read it via a FileSystemDirectoryReader retrieved by calling FileSystemDirectoryEntry.createReader(). Here's a brief example of handling the drop event:
function drop(event) {
const items = event.dataTransfer.items;
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind === 'file') {
const entry = item.webkitGetAsEntry();
if (entry.isFile) {
return new Promise((resolve, reject) => {
entry.file(
file => {
resolve(file);
},
err => {
reject(err);
}
);
});
} else if (entry.isDirectory) {
const directoryReader = entry.createReader();
return new Promise((resolve, reject) => {
directoryReader.readEntries(
entries => {
resolve(entries);
},
err => {
reject(err);
}
);
});
}
}
}
}
My coworker wrote a short article explaining the approach in more detail (disclaimer: I work for that company).
Here's a fully working Plunkr demo from the article: https://plnkr.co/edit/rFC9Ln

ng-file-upload is a well known AngularJS file upload module.
It talks about multiple file uploads. Not sure if its exactly what you want.
Heres the link ...
https://github.com/danialfarid/ng-file-upload/blob/master/README.md#user-content--usage

Flow.js allows you to upload folders of images. I haven't tried it with other file types though.

Uploading folder is not possible. You may upload single file, multiple files or zip files.
For this you may use ng-file-upload
You may get this module over here

Related

File and Directory Entries API broken in Chrome?

I'm trying to use the File and Directory Entries API to create a file uploader tool that will allow me to drop an arbitrary combination of files and directories into a browser window, to be read and uploaded.
(I'm fully aware that similar functionality can be achieved by using an file input element with webkitdirectory enabled, but I'm testing a use case where the user isn't forced to put everything into a single folder)
Using the Drag and Drop API, I've managed to read the DataTransfer items and convert them to FileSystemEntry objects using DataTransferItem.webkitGetAsEntry.
From there, I am able to tell that if the entry is a FileSystemFileEntry or a FileSystemDirectoryEntry. My plan of course if to recursively walk the directory structure, if any, which I should be able to do using the FileSystemDirectoryReader method readEntries, like this:
handleDrop(event) {
event.preventDefault();
event.stopPropagation();
//assuming I dropped only one directory
const directory = event.dataTransfer.items[0];
const directoryEntry = directory.webkitGetAsEntry();
const directoryReader = directoryEntry.createReader();
directoryReader.readEntries(function(entires){
// callback: the "entries" param is an Array
// containing the directory entries
});
}
However, I'm running into the following issue: in Chrome, the readEntries method only returns 100 entries. Apparently, this is the expected behavior as the way to obtain subsequent files from the directory is to call readEntries again. However, I'm finding this impossible to do. A subsequent call to the method throws the error:
DOMException: An operation that depends on state cached in an interface object was made but the state had changed since it was read from disk.
Does anyone know a way around this? Is this API hopelessly broken for directories of 100+ files in Chrome? Is this API deprecated? (not that it was ever "precated"). In Firefox, readEntries returns the whole directory content at once, which apparently against the spec, but it is usable.
Please advice.
Of course, as soon as I had posted this question the answer hit me. What I was trying to do was akin to the following:
handleDrop(event) {
event.preventDefault();
event.stopPropagation();
//assuming I dropped only one directory
const directory = event.dataTransfer.items[0];
const directoryEntry = directory.webkitGetAsEntry();
const directoryReader = directoryEntry.createReader();
directoryReader.readEntries(function(entries){
// callback: the "entries" param is an Array
// containing the directory entries
}, );
directoryReader.readEntries(function(entries){
//call entries a second time
});
}
The problem with this is that readEntries is asynchronous, so I'm trying to call it while it's "busy" reading the first batch (I'm sure lower-level programmers will have a better term for that). A better way of achieving what I was trying to do:
handleDrop(event) {
event.preventDefault();
event.stopPropagation();
//assuming I dropped only one directory
const directory = event.dataTransfer.items[0];
const directoryEntry = directory.webkitGetAsEntry();
const directoryReader = directoryEntry.createReader();
function read(){
directoryReader.readEntries(function(entries){
if(entries.length > 0) {
//do something with the entries
read(); //read the next batch
} else {
//do whatever needs to be done after
//all files are read
}
});
}
read();
}
This way we ensure the FileSystemDirectoryReader is done with one batch before starting the next one.

Electron will-download keeps getting interrupted

I am trying to download a file, but it keeps getting interrupted, and I have no idea why. I can not find any information on how to debug the reason it got interrupted either.
Here is where I am saving the file:
C:\Users\rnaddy\AppData\Roaming\Tachyon\games\murware\super-chain-reaction\web.zip
window.webContents.session.on('will-download', (event, item, webContents) => {
let path = url.parse(item.getURL()).pathname;
let dev = path.split('/')[3] || null;
let game = path.split('/')[4] || null;
if (!dev && !game) {
item.cancel();
} else {
item.setSavePath(Settings.fileDownloadLocation(dev, game, 'web'));
item.on('updated', (event, state) => {
let progress = 0;
if (state == 'interrupted') {
console.log('Download is interrupted but can be resumed');
} else if (state == 'progressing') {
progress = item.getReceivedBytes() / item.getTotalBytes();
if (item.isPaused()) {
console.log('Download is paused');
} else {
console.log(`Received bytes: ${item.getReceivedBytes()}; Progress: ${progress.toFixed(2)}%`);
}
}
});
}
});
Here is my listener that will trigger the above:
ipcMain.on(name, (evt) => {
window.webContents.downloadURL('http://api.gamesmart.com/v2/download/murware/super-chain-reaction');
});
Here is the output that I am getting in my console:
Received bytes: 0; Progress: 0.00%
Received bytes: 233183; Progress: 0.02%
Download is interrupted but can be resumed
I have a host file setup:
127.0.0.1 api.gamesmart.com
When I try to access the path http://api.gamesmart.com/v2/download/murware/super-chain-reaction in chrome, the file downloads just fine into my Downloads folder. So, what is causing this?
If you set the specific directory for downloading, you should use full file path with the file name in item.setSavePath() method. The best way to do it, fetching the file name from downloaditem object (item in your case) itself. You can use item.getFilename() to get the name of the current download item easily. here is the doc
And also there is a good way to get frequently used public system directory paths in electron. That is, using app.getPath(name) method. name would be the pre-defined String by electron for several directories. here is the doc
So, your complete setSavePath function would be,app.getPath("downloads") + "/" + item.getFilename()
In your case, if you are OK with your file path extraction method, only thing you are missing is filename at the end of the download path.
Of course you can use any other string as the file name if you wish. But remember to put correct extension though. :)
My solution was to use the correct Windows path separator (\), .e.g. 'directory\\file.zip'. Generally, Node.js uses / for any platform, but this seems to be sensitive about the path separator.

How to create a folder in Firebase Storage?

So the new Firebase has support for storage using Google Cloud Platform.
You can upload a file to the images folder using:
var uploadTask = storageRef.child('images').put(file, metadata);
What if you want to create a subfolder images/user1234 dynamically using code?
The offical sample does not show how to do that, nor the official guide or reference docs.
Is the Firebase Console the only place where folders can be created manually?
The Firebase Storage API dynamically creates "folders" as intermediate products: if you create a file at images/user1234/file.txt, all intermediate "folders" like "images" and "user1234" will be created along the way. So your code becomes:
var uploadTask = storageRef.child('images/user1234/file.txt').put(file, metadata);
Note that you need to include the file name (foo.txt for example) in the child() call, since the reference should include the full path as well as the file name, otherwise your file will be called images.
The Firebase Console does allow you to create a folder, since it's the easiest way to add files to a specific folder there.
But there is no public API to create a folder. Instead folders are auto-created as you add files to them.
You most certainly can create directories... with a little bit of playing with the references I did the following.
test = (e,v) => {
let fileName = "filename"
let newDirectory = "someDir"
let storage = firebase.storage().ref(`images/${newDirectory}/${fileName}`)
let file = e.target.files[0]
if(file !== undefined && file.type === "image/png" ) {
storage.put(file)
.then( d => console.log('you did it'))
.catch( d => console.log("do something"))
}
}
String myFolder = "MyImages";
StorageReference riversRef = storageReference.child(myFolder).child("images/pic.jpg");
Firebase is lacking very important functionality, there's always the need to be doing some tricks to emulate behaviours that should be standard.
If you create a folder manually from the Firebase console it will
persist even when there are no more files in it.
If you create a folder dynamically and all files get deleted at some
point, the folder will disappear and be deleted as well.
I implemented a file manager using Firebase Storage so when a user wants to upload a file he can do it through this interface not from something external to the app as is the Firebase Console. You want to give the user the option to reorganize the files as he wants, but something as common as creating a new folder cannot be done without tricks, why? just because this is Firebase.
So in order to emulate this behaviour what I came up with was the following:
Create a reference with the new folder name.
Create a reference for a "ghost" file as child of the folder's reference and give it always the same fixed name, eg. '.ghostfile'
Upload the file to this newly created folder. Any method is valid, I just use uploadString.
Every time I list the files of a reference, exclude any file named as before. So this "ghost" file is not shown in the file manager.
So an example to create a foler:
async function createFolder (currentRef: StorageReference, folderName: string) {
const newDir = ref(currentRef, name)
const ghostFile = ref(newDir, '.ghostfile')
await uploadString(ghostFile, '')
}
And an example to list the files:
async function loadLists (ref: StorageReference) {
const { prefixes, items } = await listAll(ref)
return {
directories: prefixes,
files: items.filter(file => file.name !=== '.ghostfile')
}
}
Firebase console allows you to create a folder. I don't think there is another way to create a folder.

Anyway, with js, to find a filename on the server with a wildcard?

I have a js program in which I would like to build a link to a file in a specific directory. However, the js program does not know if the file will be .html, .xls .doc or .docx or others. All it knows is the directory and the first part of the filename. I have control of that directory and there will only be one file there with that first part.
Is there anyway to do this?
No. You can try these different file endings and check if the server returns something or a 404 instead. Otherwise you have the implement some logic on the server to check the directory.
This is not a good practice and I am not recommending it, but sure it can be done: Live demo (click).
//list each file name you want to find
var files = ['some-file', 'some-other-file'];
//list possible extensions
var exts = ['html', 'doc'];
//iterate each file name
$.each(files, function(index, file) {
//attempt to get the file
//pass a copy of the "exts" array so that each function can "shift" through it
getFile(file, exts.slice());
});
function getFile(file, exts) {
//the filename to try (added extension)
var newFile = file+'.'+exts.shift();
//try to get the file
$.get(newFile).then(function(data) {
//found the file - do something with the contents
foo(data);
}, function() {
//file not found
//if there are any extensions left to try
if (exts.length) {
//try again (will shift to next extension)
getFile(file, exts);
}
});
}
function foo(data) {
var div = document.createElement('div');
div.textContent = data;
document.body.appendChild(div);
}
Only with javascript you can't access any files because security reasons reference here,
but you can create an ActiveX for Internet Explorer only.

How to resolve the C:\fakepath?

<input type="file" id="file-id" name="file_name" onchange="theimage();">
This is my upload button.
<input type="text" name="file_path" id="file-path">
This is the text field where I have to show the full path of the file.
function theimage(){
var filename = document.getElementById('file-id').value;
document.getElementById('file-path').value = filename;
alert(filename);
}
This is the JavaScript which solve my problem. But in the alert value gives me
C:\fakepath\test.csv
and Mozilla gives me:
test.csv
But I want the local fully qualified file path. How to resolve this issue?
If this is due to browser security issue then what should be the alternate way to do this?
Some browsers have a security feature that prevents JavaScript from knowing your file's local full path. It makes sense - as a client, you don't want the server to know your local machine's filesystem. It would be nice if all browsers did this.
Use
document.getElementById("file-id").files[0].name;
instead of
document.getElementById('file-id').value
I use the object FileReader on the input onchange event for your input file type! This example uses the readAsDataURL function and for that reason you should have an tag. The FileReader object also has readAsBinaryString to get the binary data, which can later be used to create the same file on your server
Example:
var input = document.getElementById("inputFile");
var fReader = new FileReader();
fReader.readAsDataURL(input.files[0]);
fReader.onloadend = function(event){
var img = document.getElementById("yourImgTag");
img.src = event.target.result;
}
If you go to Internet Explorer, Tools, Internet Option, Security, Custom, find the "Include local directory path When uploading files to a server" (it is quite a ways down) and click on "Enable" . This will work
I am happy that browsers care to save us from intrusive scripts and the like. I am not happy with IE putting something into the browser that makes a simple style-fix look like a hack-attack!
I've used a < span > to represent the file-input so that I could apply appropriate styling to the < div > instead of the < input > (once again, because of IE). Now due to this IE want's to show the User a path with a value that's just guaranteed to put them on guard and in the very least apprehensive (if not totally scare them off?!)... MORE IE-CRAP!
Anyhow, thanks to to those who posted the explanation here: IE Browser Security: Appending "fakepath" to file path in input[type="file"], I've put together a minor fixer-upper...
The code below does two things - it fixes a lte IE8 bug where the onChange event doesn't fire until the upload field's onBlur and it updates an element with a cleaned filepath that won't scare the User.
// self-calling lambda to for jQuery shorthand "$" namespace
(function($){
// document onReady wrapper
$().ready(function(){
// check for the nefarious IE
if($.browser.msie) {
// capture the file input fields
var fileInput = $('input[type="file"]');
// add presentational <span> tags "underneath" all file input fields for styling
fileInput.after(
$(document.createElement('span')).addClass('file-underlay')
);
// bind onClick to get the file-path and update the style <div>
fileInput.click(function(){
// need to capture $(this) because setTimeout() is on the
// Window keyword 'this' changes context in it
var fileContext = $(this);
// capture the timer as well as set setTimeout()
// we use setTimeout() because IE pauses timers when a file dialog opens
// in this manner we give ourselves a "pseudo-onChange" handler
var ieBugTimeout = setTimeout(function(){
// set vars
var filePath = fileContext.val(),
fileUnderlay = fileContext.siblings('.file-underlay');
// check for IE's lovely security speil
if(filePath.match(/fakepath/)) {
// update the file-path text using case-insensitive regex
filePath = filePath.replace(/C:\\fakepath\\/i, '');
}
// update the text in the file-underlay <span>
fileUnderlay.text(filePath);
// clear the timer var
clearTimeout(ieBugTimeout);
}, 10);
});
}
});
})(jQuery);
On Chrome/Chromium based apps like electron you can just use the target.files:
(I'm using React JS on this example)
const onChange = (event) => {
const value = event.target.value;
// this will return C:\fakepath\somefile.ext
console.log(value);
const files = event.target.files;
//this will return an ARRAY of File object
console.log(files);
}
return (
<input type="file" onChange={onChange} />
)
The File object I'm talking above looks like this:
{
fullName: "C:\Users\myname\Downloads\somefile.ext"
lastModified: 1593086858659
lastModifiedDate: (the date)
name: "somefile.ext"
size: 10235546
type: ""
webkitRelativePath: ""
}
So then you can just get the fullName if you wanna get the path.
Note that this would only work on chrome/chromium browsers, so if you don't have to support other browsers (like if you're building an electron project) you can use this.
I came accross the same problem. In IE8 it could be worked-around by creating a hidden input after the file input control. The fill this with the value of it's previous sibling. In IE9 this has been fixed aswell.
My reason in wanting to get to know the full path was to create an javascript image preview before uploading. Now I have to upload the file to create a preview of the selected image.
If you really need to send the full path of the uploded file, then you'd probably have to use something like a signed java applet as there isn't any way to get this information if the browser doesn't send it.
Use file readers:
$(document).ready(function() {
$("#input-file").change(function() {
var length = this.files.length;
if (!length) {
return false;
}
useImage(this);
});
});
// Creating the function
function useImage(img) {
var file = img.files[0];
var imagefile = file.type;
var match = ["image/jpeg", "image/png", "image/jpg"];
if (!((imagefile == match[0]) || (imagefile == match[1]) || (imagefile == match[2]))) {
alert("Invalid File Extension");
} else {
var reader = new FileReader();
reader.onload = imageIsLoaded;
reader.readAsDataURL(img.files[0]);
}
function imageIsLoaded(e) {
$('div.withBckImage').css({ 'background-image': "url(" + e.target.result + ")" });
}
}
seems you can't find the full path in you localhost by js, but you can hide the fakepath to just show the file name. Use jQuery to get the file input's selected filename without the path
The best solution for this, I've found, is to use a middleware like Multer. Here's a quick rundown:
npm i multer
Add enctype="multipart/form-data" to your html form.
In your backend dock where you're making your post request, require multer (const multer = require('multer'))
In the same dock, set your upload destination: const upload = multer({dest:'uploas/'}). This will automatically create a local folder called 'uploads' where your files will be added. The code I've included shows you how to upload to your local disk storage. If you're using cloud storage (e.g. AWS, Azure, Cloudinary etc.) you can check out the Multer docs to see how to manage that. There aren't too many extra steps though.
in your post request, add 'upload.single' (for one file) or 'upload.array' (for multiple files), like this:
router.post('/new', upload.single('image'), async function(req, res) { //'image' should be the name of the input you're sending in the req.body
console.log(req.file) //note, if you're using 'upload.array', this should be 'req.files'
});
the req.file will have a full path name that you can use in your post request. For more information, check out the Multer docs:
https://www.npmjs.com/package/multer
I hope this helps!
You would be able to get at least temporary created copy of the file path on your machine. The only condition here is your input element should be within a form
What you have to do else is putting in the form an attribute enctype, e.g.:
<form id="formid" enctype="multipart/form-data" method="post" action="{{url('/add_a_note' )}}">...</form>
you can find the path string at the bottom.
It opens stream to file and then deletes it.
Hy there , in my case i am using asp.net development environment, so i was want to upload those data in asynchronus ajax request , in [webMethod] you can not catch the file uploader since it is not static element ,
so i had to make a turnover for such solution by fixing the path , than convert the wanted image into bytes to save it in DB .
Here is my javascript function ,
hope it helps you:
function FixPath(Path)
{
var HiddenPath = Path.toString();
alert(HiddenPath.indexOf("FakePath"));
if (HiddenPath.indexOf("FakePath") > 1)
{
var UnwantedLength = HiddenPath.indexOf("FakePath") + 7;
MainStringLength = HiddenPath.length - UnwantedLength;
var thisArray =[];
var i = 0;
var FinalString= "";
while (i < MainStringLength)
{
thisArray[i] = HiddenPath[UnwantedLength + i + 1];
i++;
}
var j = 0;
while (j < MainStringLength-1)
{
if (thisArray[j] != ",")
{
FinalString += thisArray[j];
}
j++;
}
FinalString = "~" + FinalString;
alert(FinalString);
return FinalString;
}
else
{
return HiddenPath;
}
}
here only for testing :
$(document).ready(function () {
FixPath("hakounaMatata:/7ekmaTa3mahaLaziz/FakePath/EnsaLmadiLiYghiz");
});
// this will give you : ~/EnsaLmadiLiYghiz

Categories

Resources