1

This question concerns file handling using Java/Groovy/Grails. I am still new to these technologies, so bear with me.

I am uploading a file to Stripe after receiving the file from a user submitted POST request from the client side. I don't want to save it to the disk if it can be avoided.

I receive the file and parse into an array of bytes.

List<MultipartFile> files = ((MultipartHttpServletRequest) request).getMultiFileMap().collect {
                it.value
            }.flatten()
            try {
                if (files.size > 0) {
                    // parse file and get byte array input stream
                    MultipartFile file = files.first()
                    String fileName = file.originalFilename
                    byte[] bytes = file.inputStream.getBytes()
                    callStripeFileService(bytes)
            }

This byte array is passed to a service which makes the call to Stripe as indicated in their docs.

    callStripeFileService(byte[] bytes) {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes)
        String tempFilePath = 
             "src/main/resources/tmp/${fileName}"
        IOUtils.copy(bais, new FileOutputStream(tempFilePath))

        FileCreateParams params = FileCreateParams.builder()
            .setFile(new java.io.File(tempFilePath))
            .setPurpose(FileCreateParams.Purpose.RECEIPT)      
            .build()

        File upload = File.create(params)
    }

This is working just fine, but I dislike writing the file to disk only to have to remove it eventually. I also suspect that there must be performance implications to writing to disk.

I would love to just pass a stream to Stripe, but the .setFile() method only accepts a File or FileInputStream.

Is there any way to create a valid Java File object without saving anything to disk?

Olaf Kock
  • 46,930
  • 8
  • 59
  • 90
Adam Specker
  • 425
  • 3
  • 14
  • 1
    Maybe this will help you https://stackoverflow.com/questions/58950910/bytearrayinputstream-to-fileinputstream-without-writing-the-file-to-hard-drive. – MrFisherman Jan 08 '21 at 00:35

2 Answers2

2

No; a java.io.File is a File, hence the name. No such thing as a virtual file-like thing capturable as a File object - java.io.File is a very thin wrapper, it has one field, and that is the path, that's all it is. All methods on it, and things like new FileInputStream(...) just read the path from that File object and do all the work itself, hence making it impossible. Well designed java APIs do not use File; they use, for example, InputStream. If the stripe API only allows File, then it's badly designed API. The fix would be to send a pull request to the repo with a fix for it.

Presumably, the authors messed up; that variant that takes a FileInputStream is an error, and it should have taken an InputStream instead. I'll go on record with it, why not: A method whose parameter type is FileInputStream is broken. Not broken in the sense that 'tests can catch it', but as broken as a method that adds 2 numbers together named 'subtract'.

TL;DR: Not possible.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • I share your frustration that the method doesn't accept an input stream. I'm sure they have their reasons... Nevertheless you've given me my answer. Thank you – Adam Specker Jan 08 '21 at 17:18
  • 1
    Hey rzwitserloot, due to your suggestion the library now supports an InputSteam as an argument for setFile(). Thanks again for your help! – Adam Specker Feb 10 '21 at 18:44
2

As an alternative, you can have the user upload the file directly to Stripe so you don't have to worry about it: https://jsfiddle.net/thorstripe/bdm3j2rn/

Apparently I need to include code since there's a jsfiddle link:

console.log("Go read the jsfiddle kthx");
floatingLomas
  • 8,553
  • 2
  • 21
  • 27
  • Nice example and good suggestion, but other work needs to be done at the controller level with the byte array so this won't work for me. – Adam Specker Jan 08 '21 at 17:09