Giter Site home page Giter Site logo

multer's People

Contributors

3imed-jaberi avatar axelpale avatar carlosstenzel avatar cgmartin avatar crandmck avatar dougwilson avatar drazisil avatar dzhanavapp avatar fgadrian avatar fishrock123 avatar framp avatar fsanzv avatar gireeshpunathil avatar hacksparrow avatar hcz avatar henrique-moura avatar jonchurch avatar jpfluger avatar khacpv avatar linusu avatar maxcnunes avatar puhsiu avatar stefkors avatar tmarshall avatar tsando avatar turakvlad avatar wowzzangga avatar x-n0 avatar zengming00 avatar zloesabo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

multer's Issues

Dynamic folder Names would be Nice

Hope all is well and understand you're quite busy. But it would be great if we could pass along req.params to the destination so we could save things in folders based on parameters.

Error: ENOENT, open 'uploads/xxx'

Sometimes it will throw this error: "Error: ENOENT, open 'uploads/xxx'".
How can I avoid this?

Here is my code:

app.use(multer({
  dest: './uploads/',
  rename: function (fieldname, filename) {
    return filename;
  }
}))

Upload path : dynamic

Is it possible to assign an upload path on user base? (a kind of route per user)

Thanks
Lorenzo

Different destination folders?

Hi guys! Well, imagine a scenario where I have two forms, FormA and FormZ.

FormA the destination folder is ./uploads/customers

FormZ the destination folder is ./uploads/products

What is the best way to save my uploaded files in separate folders?

File upload sometimes hangs - multer, busboy, or something else?

HI!

I encountered some strange problem while using multer - sometimes it hangs while uploading the file.

When I am testing this behavior on my Ubuntu it succeeds every time, but when I switch to development server it hangs quite often.

I compared the behavior on both environments and added some basic printouts for troubleshooting:

app.use(multer({
  dest: './uploads/',
  rename: function (fieldname, filename) {
    return filename.replace(/\W+/g, '-').toLowerCase();
  },
  onFileUploadStart: function (file) {
    process.stderr.write('Uploading file..........');
  },
  onFileUploadComplete: function (file) {
    process.stderr.write('done\n');
  },

...

}));

On development server Uploading file......... is printed in the console but done doesn't show up - which suggests it can be multer or busboy issue.

Full code in which I am using multer can be found here:
https://github.com/aplikacjespoleczne/WizualizacjaBudzetu

Also for troubleshooting I've added the simmilar logging to the multer code (in index.js) :

        fileStream.on('data', function(data) {
          console.log('Next part received!');        // ADDED
          if (data) { file.size += data.length; }
          // trigger "file data" event
          if (options.onFileUploadData) { options.onFileUploadData(file, data); }
        });

And the difference between Ubuntu and development server is the last chunk of data. In both environments Next part received! shows up but on development server done doesn't - which may suggest that the 'finish' event is not emitted.

Can it be multer or busboy? Can you give me some advice where to look further? What else could I trace?

Thanks!

Supporting PUT requests?

In multer/Express 4 support for PUT requests has been removed. Multer should parse PUT requests as well as POST ones.

req.files.fieldname is not an array

Hi,
I'm really new to all this so I apologize if this is a really stupid question
Is there any particular reason why
if (req.files[field].length === 1) {
req.files[field] = req.files[field][0];
}
is included in the onFinish function?

This is causing the length attribute to become undefined.
This is a small issue as I use the length to traverse through the entire array in case multiple files are sent.

Non changing file problem

I have been searching for a replacement for connect.multipart and found multer. Thank you for this.

I have been testing it in my project and I have found this, if you submit a form without having filled every file field, you get an error in index.js:52 because of the absence of filename so split throws an exception.

It can be avoided assigning an empty value if undefinned. I'm new to forks and pull request in Github but I'm trying to do a pull request.

Error: "Cannot switch to old mode now."

I'm using node.js version v0.10.28, ExpressJS v4.3.0 and Multer v0.0.7

When I send any files to a route which accepts multipart form data I get the error above. It was working perfectly fine, I'm not sure what's changed. ๐Ÿ˜•

Why the decision to not add blank submitted fields to req.body?

Am current switching my project from using connect-multiparty to multer which in general was pretty painless except for one issue that I have where fields that are submitted with blank values aren't added to req.body and one of my pieces of code was expecting them to be there.

For instance if I had the following input field:

<input type="text" id="name" name="conversation[name]" />

And this was submitted with a blank value I would still expect the req.body to be:

console.log(req.body)
// { conversation: { name: '' } }

But I actually get:

console.log(req.body)
// {}

This is because of the code on line 58 of index.js returning if there is no value set.

// don't attach to the body object, if there is no value
if (!val) return;

Although it was no trouble to resolve, I was expecting those fields to be on the req.body even if they weren't filled in.

So I just wanted to know why the decision to not add blank submitted fields to req.body? Or is there a pattern that people are using that I'm not aware of?

My 'fix' was to add the following line to my module:

req.body.conversation = req.body.conversation || {};

But it seems that if I come across this again I'll have to duplicate this process again for any occurrences where this is a problem.

Would it make sense to make this an optional setting so that the user can decide if they want to add them to req.body or not?

Something along the lines of:

if (options.stripBlank && !val) return;

Interested to know what you think and let me know if you want a pull request for this.

No simple way to tell the client if an error occured

All of the error handling functions, onFileSizeLimit, onError etc... have no way of either telling the client that an error has happened or passing the fact an error happened to the next middle ware:

  router.use('/uploads', multer({
    dest: './uploads',
    limits: { files: 2 },
    onFilesLimit: function () {
      // No way to add to the request that an error occurred, or respond to the client
   }
  });

  router.post('/uploads', function(req, res) {
    // Cannot tell that any of the limits has been reached so cannot inform the client
  });

You probably don't want to respond to the client directly when a limit has been hit, but you should at least be able to tell that an error occurred later on.

Something along the lines of:

  router.use('/uploads', multer({
    dest: './uploads',
    limits: { files: 2 },
    onFilesLimit: function (file_data) {
      file_data.files_limit_reached = true;
   }
  });

  router.post('/uploads', function(req, res) {
    if (file_data.files_limit_reached) {
        res.json({ error: "file limit reached" });
    }
  });

So that rather then removing the files that have hit an error or exceeded a limit they are flagged with an error message.

Finalize the reception on client side

Hi,

Thanks for this great module that might save me a lot of time. Unfortunately I'm new to all of this and it wasn't clear to me that I had to send a status code 200 to make the client enter its success callback.

Maybe a line about that and an example would be nice.

I am not too sure but something like that :

app.post "/api/files/upload", (req, res) ->
   console.log ('upload done for ' + req.files.file.name)
   res.setHeader "Content-Type:", "application/json"
   res.send 200, {myFileReference: req.files.file.name}

Nicolas

async rename

Hello,

I noticed the rename function uses a return value. Can this be done inside a callback? or would it simply return undefined? I need to fetch an ID for the image which would be a database call like so:

rename: function (fieldname, filename) {
  getNextPhotoId(function(id){
    return id + ".jpg";
  }
}

is this possible with multer?

Dynamically changing destination folder name

Is it possible to dynamically change the destination folder name depending on the field name? I made a quick test which failed

app.use(multer({
  dest: './uploads/',
  rename: function (fieldname, filename) {
      if(fieldname == 'avatar_file'){
          return filename.replace(/\W+/g, '-').toLowerCase() + Date.now()
      } else {
          return 'avatars/' + filename.replace(/\W+/g, '-').toLowerCase() + Date.now()
      }
  }
}))

Thanks

Multer emits 'end' and chains to the next middleware prematurely when parsing file parts

Multer uses the busboy 'end' event to fire its own 'onParseEnd' event and cede control to the next registered middleware in the chain. The problem is that busboy fires this event when it is finished parsing the form data and firing events related to the parts it encounters but hasn't necessarily finished writing out any files attached to the form it's just parsed.

Downstream middleware modules that immediately open these files see inconsistent file contents (truncated, in other words) depending on when they open the files. Sometimes the files are written out in time for the next middleware to be able to use them completely, sometimes not.

Here is additional detail from the busboy issue list about this problem in general, and suggestions for fixes: mscdex/busboy#1. To be clear, it is not currently considered a busboy problem fixable within busboy itself, but rather an API usage issue.

Required Event: onFileSizeLimit

Busboy provided fileSizeLimit for limits, and it emit 'limit' event on fileStream when it reach the limit, but on the multer, it doesn't have a listener to this event, which is needed I think.
Or, the file will just stop uploading and left broken without any error when reach the limit, it requires to be handled.
I read your code and add this next to your many fileStream.on():

    fileStream.on('limit', function() {
      if (options.onFileSizeLimit) { options.onFileSizeLimit(); }
    })

I hacked this for I needed right now, please add this to your code and update it later.

Writing files to the disk

Currently files seem to always be written to disk, although with the new inMemory option this my not be needed. Writing files to the disk just to delete them again is both useless and unnecessary. Therefore a "writeToDisk" option would be useful.

I suggest to use !inMemory as the standard value for this option, because this is (from my perspective) the expected behavior.

readme.md has some problems

onFilesLimit()

Event handler triggered when the number of files exceed the specification in the limit object. No more files will be parsed after the limit is reached.

Example:

onFilesLimit = function() {
console.log('Crossed file limit!');
};
onFieldsLimit()

Event handler triggered when the number of fields exceed the specification in the limit object. No more fields will be parsed after the limit is reached.

Example:

onFilesLimit = function() {
console.log('Crossed fields limit!');
};
onPartsLimit()

Event handler triggered when the number of parts exceed the specification in the limit object. No more files or fields will be parsed after the limit is reached.

Example:

onFilesLimit = function() {
console.log('Crossed parts limit!');
};

Large File Handling (eg 2.5 MB file) Slower When InMemory is True

When inMemory is true and file.buffer is larger than 64 Kb, there is an "issue" that I don't think is fixable right now b/c it's the way Node.js currently allocates memory for large buffers. (see StackOverflow and this blog)

In multer/master, I have mocha tests (see test/inmemory.js) that can be used to test this issue. When running, the multi-file upload test take an average of 1 second. Compare this to the write-to-disk multi-file upload taking 100 ms. In local tests, I changed-up my inMemory Buffer code to a writable stream and piped the filestream directly to it but still would get 1 second performance. I don't think my test hardware is to blame (i7-4810MQ w/ 32 Gig RAM on SSDs).

I also ran the tests against the latest node in development 0.11.14 but there's a bug preventing mocha from completing. Need to keep an eye on this b/c TJ wrote "[there's been] significant performance work done in core features (Buffers, TLS, streams)." reference

Hopefully I'm wrong with this issue and someone knowing more about how core streams/Buffers work can suggest ways to improve inMemory performance for large buffers.

support multiple files

I have this in my form:

<input accept="image/svg+xml" name="file" id="file" type="file" multiple>

It correctly lets me select multiple SVG files, and I am doing client-side HTML5 stuff, and I can read multiple files. I'd prefer to not have to base64-encode them, client-side. The req.files I get back looks like this:

{ "file": 
   { "fieldname": "file",
     "originalname": "6.svg",
     "name": "ce21d25aee1282a3fe654c10976434b1.svg",
     "encoding": "7bit",
     "mimetype": "image/svg+xml",
     "path": "uploads/ce21d25aee1282a3fe654c10976434b1.svg",
     "extension": "svg" } }

file should be an array.

Request occasionally hangs if files limit is exceeded

The following code works if you try to upload 2 or less files but the request hangs if you upload 3 or more files.

var express = require('express');
var multer = require('multer');
var app = express();

app.use('/upload', multer({
    dest: 'upload',
    limits: { files: 2 }
}));

app.post('/upload', function(req, res) {
  res.json({files: req.files});
});

var server = app.listen(3001, function() {
  console.log('Express server listening on port ' + server.address().port);
});

On aborting an upload

Is there a way of catching aborting event (e.g. user shut down the browser while uploading)?

InMemory tests failing on v0.11 (Travis / Mocha)

multer/master passes Travis with node v0.10. But does not for v0.11. In mocha tests, the blowups happen on line 64 test/inmemory.js, where the inMemory buffer is compared to the file size.

expect(form.files.small0.buffer.length).to.equal(form.files.small0.size);

form.files.small0.bufferis an object but lacks a.length` property. In fact, instantiating a new buffer from this one

new Buffer(new Buffer(form.files.small0.buffer)

does give me a valid .length property.

Thus, the following test code will work:

if ( form.files.small0.buffer !== undefined ) {
    console.log( new Buffer(form.files.small0.buffer).length);
    form.files.small0.buffer = new Buffer(form.files.small0.buffer);
}
console.log( 'file length=' + form.files.small0.size + '    buffer length = ' + form.files.small0.buffer.length );
expect(form.files.small0.buffer.length).to.equal(form.files.small0.size);

I have verified that index.js does assign a valid file.buffer property before ending the filestream and file.buffer.length is accessible.

FILEEND: file length=1778    buffer length = 1778
ONFINISH: file length=1778    buffer length = 1778

Question(s)

  1. I can't understand why the parameters would be modified between creation and sending? It could be on how v11 receives the request?

Update:

res.send sends req.body and req.files to the server as a JSON object. I suspect the problem is in how the Buffer is (not created) by JSON on receive. But why the different JSON treatment between 0.10 and 0.11? Unless someone has some ideas, I'll go hunting for that after I get something to eat.

Update 3

That's exactly what's happening. JSON.parse() is done differently in v0.11. It can be tested for in multer by diving down into node_modules/express/lib/response.js and at line 229, just after var body = JSON.stringify(val, replacer, spaces);, insert the following:

var restored = JSON.parse(body);

if ( restored.files !== undefined ) {
    console.log( 'RESPONSE A: file length=' + val.files.small0.size + '    buffer length = ' + val.files.small0.buffer.length );
    console.log( 'RESPONSE B: file length=' + restored.files.small0.size + '    buffer length = ' + restored.files.small0.buffer.length );
}

Set different destination path

I'm not getting how to set different destination path for say different routes?
Example : app.post('/api/upload', function (req, res) { // here i want destination as dest: './static/uploads/'

and app.post('/api/pic', function (req, res) { // here i want destination as dest: './static/pic/

and app.post('/api/pdf', function (req, res) { // here i want destination as dest: './static/pdf/'

Using multer inside controllers/models

Folks,

Host can I invoke the onFileUploadComplete method from my controller?

server.js

server.use(multer({ dest: '/tmp'}));
server.post('/users/:id/avatar', avatar.post);

avatar.js

post: function (req,res) {
    var image = req.files;
    console.log (req.files);

    onFileUploadComplete: function (file) {
        console.log(file.fieldname + ' uploaded to  ' + file.path);
       ....
         responses.Success('wroteToDB',res);
    }
},

Thanks!

Error - csurf with multer

Hello, im using csrf module (csurf) but when im uploading...

Error: invalid csrf token
   at verifytoken (D:\nodejs\node_modules\csurf\index.js:234:13)
   at csrf (D:\nodejs\node_modules\csurf\index.js:94:7)
   at Layer.handle [as handle_request] (D:\nodejs\node_modules\express\lib\router\layer.js:76:5)
   at trim_prefix (D:\nodejs\node_modules\express\lib\router\index.js:263:13)
   at D:\nodejs\node_modules\express\lib\router\index.js:230:9
   at Function.proto.process_params (D:\nodejs\node_modules\express\lib\router\index.js:305:12)
   at D:\nodejs\node_modules\express\lib\router\index.js:221:12
   at Function.match_layer (D:\nodejs\node_modules\express\lib\router\index.js:288:3)
   at next (D:\nodejs\node_modules\express\lib\router\index.js:182:10)
   at methodOverride (D:\nodejs\node_modules\method-override\index.js:77:5)`

var product=config.upload;

 uri.route('/up').get(function (req,res,next) {
              res.render('upload',{
                csrf  : req.csrfToken()
              });
              res.end();
      }).post(product,function  (req,res,next) {
        console.log('IN POST (/upload)');
        console.log(req.body)
        var filesUploaded = 0;
        if ( Object.keys(req.files).length === 0 ) {
            console.log('no files uploaded');
        } else {
            console.log(req.files)

            var files = req.files.file1;
            if (!util.isArray(req.files.file1)) {
                files = [ req.files.file1 ];
            } 

            filesUploaded = files.length;
        }
        res.json({ message: 'Finished! Uploaded ' + filesUploaded + ' files.  Route is /upload' });
      });

๐Ÿ”จ

inMemory Question

If inMemory is set to true, when or how would the file be removed from memory?

(Sorry, if it's a dumb question.)

Is there a way to access the file path and original file name in the rename function?

I try to rename an incoming file chunk accessing the filename parameter, but the filename is set to "blob"!! I think i misunderstand the hole concept of file uploading.... in my mind the process is something like this: the file is sent from client to server one chunk at a time that is stored in a temporary folder and after completion i must resemble this temporary chunks into the sent file and move it to a permanent folder! I was using Flow.js to allow a "smarter" upload process where the client check what chunks was already uploaded and start from there in case of some previous failure, but i'm having trouble to integrate multer with Flow.js to have this nice functionality on! Anyone could help my stupidity in this matter? :)

Async call in onFileUploadStart

Currently there exists the ability to stop an upload by returning false.

onFileUploadStart: function (file) {
  if (file.originalname == 'virus.exe') return false;
}

I'm suggesting the ability to provide a function with two arguments where the second argument is a callback. If cb(false) is called the file is not uploaded.

onFileUploadStart: function (file, cb) {
  somethingAsync(function(err) {
    if(err) return cb(false);
    // Do something
    return cb();
  });
}

There may be a better away of approaching this, or a reason this wouldn't work. Input is appreciated. Otherwise I can work on submitting a PR.

Access to state during event flow

Hi,

First off, great plugin, using it for a POC and does what it says on the tin!

Just a question in relation to how I can access output generated as part of the multer step or to pass objects into it!

For example;

  1. User posts many files to an api/upload endpoint
  2. Mutler kicks in and as part of the onFileUploadComplete event, triggers the images to be compressed
  3. If all the events in Multer are successful, the user receives a valid 200 with a JSON response
  4. Mongdb is updated with the results and images are both stored in static directory and in database as a base64 string

However, as part of the onFileUploadComplete event i need the output generated to be added to an existing table or at least added to already created object that will be saved when Multer is completed.

Your advice would be appreciated on how to either store the compressed image to the db by passing in the new mongo model during the onFileUploadComplete step or by returning the compressed image so that it can be done outside of the Mutler tasks.

J

regards limits in multer

when we upload file thats bigger than "fileSize" limit. multer can store that file with new limited size. How we get limit of file exceed ??
And one simple one, what is the unit type we specify in limits ?

Set different upload directory based on user id [question]

Hello there.
In my web app all the users have a unique id, and i want the uploaded images to be stored in a directory named based on their id.

Example:

.public/uploads/3454367856437534

Here is a part of my routes.js file:


// load multer to handle image uploads
var multer = require('multer');
var saveDir = multer({
dest: './public/uploads/' + req.user._id, //error, we can not access this id from here
onFileUploadStart: function (file) {
return utils.validateImage(file); //validates the image file type
}
});

module.exports = function(app, passport) {

app.post('/', saveDir, function(req, res) {
id : req.user._id, //here i can access the user id
});
});

}

How can i access the "req.user._id"

outside of the "function(req,res)"

So i can use it with multer to generate the proper directory based on the id?

In other words, i want to handle the destination of the uploaded file in callback function (req,res)

Stop/Prevent file upload if user not authorized

I'm looking for a way to prevent upload in case of not authenticated user

I'm using Express which is configured to use passport as auth middelware.

I thought to use onFileUploadStart to reject the file, but I cannot find a link with "request" object, with which it would be possible to match the user.

Thanks
Lorenzo

Is inMemory option ready to be published on NPM?

I was attempting to use the inMemory option as described in the documentation, against the latest version on NPM with no success, until I realized that inMemory was fairly new and while available on this repo, it has not been published to NPM yet.

It looks like the latest version on NPM is 0.1.4, while this repo is up to 0.1.6. Are you planning on publishing on NPM soon to make the inMemory feature available without needing to reference a Github commit for installation?

Upload security

This is not an issue with multer directly, but more an issue with applications using multer.

There is little to no mention of any security issues with file uploads handled by multer leading to a lot of example code around the web based off the simple examples in the readme that completely ignore any security issues, notably things like:

var express = require('express')
var multer  = require('multer')

var app = express()
app.use(multer({ dest: './uploads/'}))

app.post('/api/uploads', ...handles maby one expected file...)

The first major issue here is that multer will be executed on any post request, even for post requests that are not expected to handle files, meaning that you could potentially upload files to the server bypassing the upload url if there exists another post url.

This is a major concern as if people are using these examples directly in their code they will be vulnerable to to file upload exploits if they do not already know about them.

The example should probably be changed to app.use('/api/upload', multer({ dest: './uploads/'})) so that multer only handles files to locations that are known to handle uploads.

It also might be worth adding a filter on what files are expected so that the user does not have to worry about unexpected files being uploaded. Something long the lines of:

app.use('/api/uploads', multer({ dest: './uploads', filter: ['someFile']})

so that multer only handles files found in the POST requests someFile variable and rejects/errors for any other files found. This could be partly done by limits.files, but this does not ensure that the expected file ends up in the right variable, and people generally only check for what they are expecting.

Body parser array

Hi!

First, congratulations for this middleware, it's awsome and simple!

Look my issue as a improvement for multer.

I have some html inputs and they are in array, for example (in Jade):

input(name="team[0]name")
input(name="team[0]leaderName")

input(name="team[1]name")
input(name="team[1]leaderName")

When I process this code, my response is:

{
    team[0]name: "John",
    team[0]leaderName: "Brad",
    team[1]name: "Mike",
    team[1]leaderName: "Leo"
}

In this case, if the structure of the response was changed to the following structure, would be better?

{
    team: [
        {
            name: "John",
            leaderName: "Brad"
        },
        {
            name: "Mike",
            leaderName: "Brad"
        }
    ]
}

Thanks! (sorry my english)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.