Zip File Breaks with python 3.10 Beta

What I’m trying to do:
Use zipfile to zip files uploaded by a user.

What I’ve tried and what’s not working:
It works with the regular python3 enviroment, but breaks in python 3.10 beta minimal. All the libraries import fine. Is there something I’m missing?
Code Sample:

import anvil.server
import anvil.media
from anvil.tables import app_tables
import shutil
import tempfile

@anvil.server.callable
def make_zip_remote(file,zip_file_name):
  with tempfile.TemporaryDirectory() as temp_dir:
    #
    # Need to Create File with open to control file name
    #
    with open(f'/tmp/{file.name}','w+') as temp_file: #TODO: BECARE BEFORE SELLING TO OTHER COMPANY. if I DONT USE tempfile, temporary files with file names that arent unique could be shared throughtout my account
      #
      # Need to make pdf a media blob
      #
      print(file.name)
      anvil.media.write_to_file(file,f'{temp_file.name}')
      shutil.copy(f'{temp_file.name}',f'{temp_dir}')
    my_zip   = shutil.make_archive(zip_file_name, 'zip', f'{temp_dir}')
    tmp_file = anvil.media.from_file(f'{my_zip}', 'text/plain')
    app_tables.files.add_row(file=tmp_file)

error

_1_ Messenger.png

`OSError: [Errno 30] Read-only file system: 'Test.zip'`

* `at /usr/local/lib/python3.10/zipfile.py:1251`
* `called from /usr/local/lib/python3.10/shutil.py:983`
* `called from /usr/local/lib/python3.10/shutil.py:1124`
* `called from Zip, line 33`
* `called from Form1, line 14`

Clone link:
share a copy of your app

Might be something to do with this maybe?

Changed in version 3.10.6: This function is now made thread-safe during creation of standard .zip and tar archives.

Taken from here :

Don’t know what, but something changed in v 3.10 which might explain why it’s doing something different.

This has got to be one of the lowest quality replies I’ve ever given … bed time…

2 Likes

I’m on my phone now. I had the same problem about a month ago. Try using byte stream instead of files.

1 Like

Thank you for replying! Could you elaborate more?

I tried doing the following, but I definitely and implementing wrong.

import anvil.server
import anvil.media
from anvil.tables import app_tables
import shutil
import tempfile
import io

@anvil.server.callable
def make_zip_remote(file,zip_file_name):
  with tempfile.TemporaryDirectory() as temp_dir:
    #
    # Need to Create File with open to control file name
    #
    with open(f'/tmp/{file.name}','w+') as temp_file: #TODO: BECARE BEFORE SELLING TO OTHER COMPANY. if I DONT USE tempfile, temporary files with file names that arent unique could be shared throughtout my account
      #
      # Need to make pdf a media blob
      #
      print(file.name)
      buffer = io.BytesIO(file.get_bytes())
      buffer.seek(0)
      anvil.media.write_to_file(buffer,f'{temp_file.name}')
      shutil.copy(f'{temp_file.name}',f'{temp_dir}')
    my_zip   = shutil.make_archive(zip_file_name, 'zip', f'{temp_dir}')
    tmp_file = anvil.media.from_file(f'{my_zip}', 'text/plain')
    app_tables.files.add_row(file=tmp_file) 

I give up :slight_smile:

Ignore me…

1 Like

Thanks for trying, I saw your code and had hope :smiling_face_with_tear:

I’m having one of those 24 hours where I type faster than I think. I’ll have it shortly I reckon :slight_smile:

here’s a working version:



@anvil.server.callable
def make_zip_remote(file, zip_file_name):
    if not zip_file_name.endswith(".zip"):
        zip_file_name += ".zip"

    with tempfile.TemporaryDirectory() as temp_dir:
        with anvil.media.TempFile(file) as f_name:
            shutil.copy(f_name, temp_dir + "/" + file.name)

        with anvil.media.TempFile() as z_name:
            my_zip = shutil.make_archive(z_name, 'zip', root_dir=temp_dir)
            tmp_file = anvil.media.from_file(my_zip, 'application/zip', zip_file_name)
            app_tables.files.add_row(file=tmp_file)


2 Likes

I knew if I messed it up enough, a grown up would feel compelled to do it properly :slight_smile:

I’m off for my medication …

1 Like

:face_holding_back_tears:

The hours of my life…

THANK YOU @stucork !

2 Likes

The version I use all the time: (…in the style of your function)

import zlib

@anvil.server.callable
def make_zip_remote(file,zip_file_name):

    tmp_file = anvil.BlobMedia(
                                content_type='application/zip', 
                                content=zlib.compress(file.get_bytes(), level=9),
                                name=zip_file_name,
                                   )

    app_tables.files.add_row(file=tmp_file) 

4 Likes

That’s basically what I was doing - minus the brevity & simplicity - whilst also being unencumbered with the onerous weight of successfully achieving the goal :crazy_face:

Ah I just looked at the code you edited away, and yeah I moved on to zlib after using zipfile for many years for 2 reasons:

  1. zipfile has horrible support for file streams and the library wants a place to write to disk all the time even if it isn’t using it for anything.
  2. zlib is also available in javascript so we can move the compression/decompression to the client browser/cpu if the use case involves ton’s of users all hogging your anvil server module resources by zipping files that have to be transferred to or from the server module uncompressed, then worked on.
1 Like

I would LOVE to move this client-side!

Thank you for the suggestion. Do you know if it is part of the skript library? or do we have to do some JavaScript interfacing to get the job done?

I avoid javascript like the plague and even I was able to use it with anvil by just following almost any of the docs for how to use javascript with anvil:

Edit: I’m going to make a clone example.

1 Like

(I was being silly - mine was rubbish and didn’t work at all - half me, half chatgpt, half wit… :slight_smile:

Sorry for my late response. My solution you can’t move to client side anyway.

        import zipfile#creation of zip files

        zip_filename = "files.zip"
        # Create a BytesIO object to hold the zip archive
        zip_buffer = BytesIO()

        # Create the zip archive in memory
        with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_archive:
      

          # Your existing code to add the files to zip
          # y all files
          for file_ready in y:   
             
              # In case of reading from a database: Convert LazyMedia to BytesIO
                 file_ready = BytesIO()
                 file_ready.write(media_object.get_bytes())
                 file_ready.seek(0)
      
              # Add the file_ready to the zip archive with the filename
                zip_archive.writestr(filename_in_zip, file_ready.getvalue())

          zip_archive.close()
          zip_buffer.seek(0, 0)
          
          # Upload file to Anvil server
          file =anvil.BlobMedia("application/zip", content=zip_buffer.read(), name=now.strftime(f"%Y%m%d-%H-%M-%S-zip-files.zip")
1 Like

No need for apologies! you’re response is very appreciated!

I posted the clone example here:

2 Likes